summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
committerHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
commit693cdce08e14833f26f4e8a1f26e4fd546be4c35 (patch)
tree7667d2223dc32d9f21b5b60ab200e64ed46e3e20 /src/class
parent41e9eaa65a935136085d78ec4b99c81ff991b560 (diff)
parentcd3561bf158afd5a5718904b8139a338d1e3b67c (diff)
Merge remote-tracking branch 'tinyusb/master' into pr-osal-spin-deinit
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio.h1822
-rw-r--r--src/class/audio/audio_device.c842
-rw-r--r--src/class/audio/audio_device.h159
-rwxr-xr-xsrc/class/bth/bth_device.c6
-rwxr-xr-xsrc/class/bth/bth_device.h6
-rw-r--r--src/class/cdc/cdc.h22
-rw-r--r--src/class/cdc/cdc_device.c483
-rw-r--r--src/class/cdc/cdc_device.h117
-rw-r--r--src/class/cdc/cdc_host.c668
-rw-r--r--src/class/cdc/cdc_host.h79
-rw-r--r--src/class/cdc/cdc_rndis.h6
-rw-r--r--src/class/cdc/cdc_rndis_host.c289
-rw-r--r--src/class/cdc/serial/ftdi_sio.h13
-rw-r--r--src/class/dfu/dfu.h6
-rw-r--r--src/class/dfu/dfu_device.c49
-rw-r--r--src/class/dfu/dfu_device.h6
-rw-r--r--src/class/dfu/dfu_rt_device.h6
-rw-r--r--src/class/hid/hid.h6
-rw-r--r--src/class/hid/hid_device.c6
-rw-r--r--src/class/hid/hid_device.h10
-rw-r--r--src/class/hid/hid_host.c128
-rw-r--r--src/class/hid/hid_host.h75
-rw-r--r--src/class/midi/midi.h18
-rw-r--r--src/class/midi/midi2_device.c769
-rw-r--r--src/class/midi/midi2_device.h193
-rw-r--r--src/class/midi/midi2_host.c635
-rw-r--r--src/class/midi/midi2_host.h107
-rw-r--r--src/class/midi/midi_device.c609
-rw-r--r--src/class/midi/midi_device.h157
-rw-r--r--src/class/midi/midi_host.c144
-rw-r--r--src/class/midi/midi_host.h69
-rw-r--r--src/class/msc/msc.h6
-rw-r--r--src/class/msc/msc_device.c95
-rw-r--r--src/class/msc/msc_device.h6
-rw-r--r--src/class/msc/msc_host.c48
-rw-r--r--src/class/msc/msc_host.h48
-rw-r--r--src/class/mtp/mtp.h68
-rw-r--r--src/class/mtp/mtp_device.c120
-rw-r--r--src/class/mtp/mtp_device.h16
-rw-r--r--src/class/net/ecm_rndis_device.c30
-rw-r--r--src/class/net/ncm.h18
-rw-r--r--src/class/net/ncm_device.c220
-rw-r--r--src/class/net/net_device.h33
-rw-r--r--src/class/printer/printer.h (renamed from src/class/cdc/cdc_rndis_host.h)53
-rw-r--r--src/class/printer/printer_device.c328
-rw-r--r--src/class/printer/printer_device.h151
-rw-r--r--src/class/usbtmc/usbtmc.h4
-rw-r--r--src/class/usbtmc/usbtmc_device.c16
-rw-r--r--src/class/vendor/vendor_device.c367
-rw-r--r--src/class/vendor/vendor_device.h167
-rw-r--r--src/class/vendor/vendor_host.h6
-rw-r--r--src/class/video/video.h10
-rw-r--r--src/class/video/video_device.c219
-rw-r--r--src/class/video/video_device.h19
54 files changed, 6198 insertions, 3355 deletions
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 0d1acadcc..428391bb2 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -30,8 +30,8 @@
* Currently only MIDI subclass is supported
* @{ */
-#ifndef _TUSB_AUDIO_H__
-#define _TUSB_AUDIO_H__
+#ifndef TUSB_AUDIO_H__
+#define TUSB_AUDIO_H__
#include "common/tusb_common.h"
@@ -39,917 +39,1241 @@
extern "C" {
#endif
-/// Audio Device Class Codes
+//--------------------------------------------------------------------+
+// GENERIC AUDIO CLASS CODES (COMMON TO UAC1 AND UAC2)
+//--------------------------------------------------------------------+
/// A.2 - Audio Function Subclass Codes
-typedef enum
-{
+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
+typedef enum {
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V1 = 0x00,///< Version 1.0 - same as undefined for backward compatibility
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20,///< Version 2.0
} audio_function_protocol_code_t;
/// A.5 - Audio Interface Subclass Codes
-typedef enum
-{
+typedef enum {
AUDIO_SUBCLASS_UNDEFINED = 0x00,
- AUDIO_SUBCLASS_CONTROL , ///< Audio Control
- AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
- AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
+ AUDIO_SUBCLASS_CONTROL, ///< Audio Control
+ AUDIO_SUBCLASS_STREAMING, ///< Audio Streaming
+ AUDIO_SUBCLASS_MIDI_STREAMING,///< MIDI Streaming
} audio_subclass_type_t;
/// A.6 - Audio Interface Protocol Codes
-typedef enum
-{
- AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
- AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+typedef enum {
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V1 = 0x00,///< Version 1.0 - same as undefined for backward compatibility
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20,///< Version 2.0
} audio_interface_protocol_code_t;
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types
+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
+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
+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
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 1.0 (UAC1) DEFINITIONS
+//--------------------------------------------------------------------+
+
+/// A.5 - Audio Class-Specific AC Interface Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO10_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO10_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO10_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO10_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO10_CS_AC_INTERFACE_PROCESSING_UNIT = 0x07,
+ AUDIO10_CS_AC_INTERFACE_EXTENSION_UNIT = 0x08,
+} audio10_cs_ac_interface_subtype_t;
+
+/// A.6 - Audio Class-Specific AS Interface Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO10_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+} audio10_cs_as_interface_subtype_t;
+
+/// A.8 - Audio Class-Specific EP Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO10_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio10_cs_ep_subtype_t;
+
+/// A.9 - Audio Class-Specific Request Codes UAC1
+typedef enum {
+ AUDIO10_CS_REQ_UNDEF = 0x00,
+ AUDIO10_CS_REQ_SET_CUR = 0x01,
+ AUDIO10_CS_REQ_GET_CUR = 0x81,
+ AUDIO10_CS_REQ_SET_MIN = 0x02,
+ AUDIO10_CS_REQ_GET_MIN = 0x82,
+ AUDIO10_CS_REQ_SET_MAX = 0x03,
+ AUDIO10_CS_REQ_GET_MAX = 0x83,
+ AUDIO10_CS_REQ_SET_RES = 0x04,
+ AUDIO10_CS_REQ_GET_RES = 0x84,
+ AUDIO10_CS_REQ_SET_MEM = 0x05,
+ AUDIO10_CS_REQ_GET_MEM = 0x85,
+ AUDIO10_CS_REQ_GET_STAT = 0xFF,
+} audio10_cs_req_t;
+
+/// A.10.1 - Terminal Control Selectors UAC1
+typedef enum {
+ AUDIO10_TE_CTRL_UNDEF = 0x00,
+ AUDIO10_TE_CTRL_COPY_PROTECT = 0x01,
+} audio10_terminal_control_selector_t;
+
+/// A.10.2 - Feature Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_FU_CTRL_UNDEF = 0x00,
+ AUDIO10_FU_CTRL_MUTE = 0x01,
+ AUDIO10_FU_CTRL_VOLUME = 0x02,
+ AUDIO10_FU_CTRL_BASS = 0x03,
+ AUDIO10_FU_CTRL_MID = 0x04,
+ AUDIO10_FU_CTRL_TREBLE = 0x05,
+ AUDIO10_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO10_FU_CTRL_AGC = 0x07,
+ AUDIO10_FU_CTRL_DELAY = 0x08,
+ AUDIO10_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO10_FU_CTRL_LOUDNESS = 0x0A,
+} audio10_feature_unit_control_selector_t;
+
+/// A.10.3.1 - Up/Down-mix Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_UD_CTRL_UNDEF = 0x00,
+ AUDIO10_UD_CTRL_ENABLE = 0x01,
+ AUDIO10_UD_CTRL_MODE_SELECT = 0x02,
+} audio10_up_down_mix_control_selector_t;
+
+/// A.10.3.2 - Dolby Prologic Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_DP_CTRL_UNDEF = 0x00,
+ AUDIO10_DP_CTRL_ENABLE = 0x01,
+ AUDIO10_DP_CTRL_MODE_SELECT = 0x02,
+} audio10_dolby_prologic_control_selector_t;
+
+/// A.10.3.3 - 3D Stereo Extender Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_3D_CTRL_UNDEF = 0x00,
+ AUDIO10_3D_CTRL_ENABLE = 0x01,
+ AUDIO10_3D_CTRL_SPACIOUSNESS = 0x02,
+} audio10_3d_stereo_extender_control_selector_t;
+
+/// A.10.3.4 - Reverberation Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_RV_CTRL_UNDEF = 0x00,
+ AUDIO10_RV_CTRL_ENABLE = 0x01,
+ AUDIO10_RV_CTRL_REVERB_LEVEL = 0x02,
+ AUDIO10_RV_CTRL_REVERB_TIME = 0x03,
+ AUDIO10_RV_CTRL_REVERB_FEEDBACK = 0x04,
+} audio10_reverberation_control_selector_t;
+
+/// A.10.3.5 - Chorus Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_CH_CTRL_UNDEF = 0x00,
+ AUDIO10_CH_CTRL_ENABLE = 0x01,
+ AUDIO10_CH_CTRL_CHORUS_LEVEL = 0x02,
+ AUDIO10_CH_CTRL_CHORUS_RATE = 0x03,
+ AUDIO10_CH_CTRL_CHORUS_DEPTH = 0x04,
+} audio10_chorus_control_selector_t;
+
+/// A.10.3.6 - Dynamic Range Compressor Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_DR_CTRL_UNDEF = 0x00,
+ AUDIO10_DR_CTRL_ENABLE = 0x01,
+ AUDIO10_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO10_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO10_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO10_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO10_DR_CTRL_RELEASE_TIME = 0x06,
+} audio10_dynamic_range_compression_control_selector_t;
+
+/// A.10.4 - Extension Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_XU_CTRL_UNDEF = 0x00,
+ AUDIO10_XU_CTRL_ENABLE = 0x01,
+} audio10_extension_unit_control_selector_t;
+
+/// A.10.5 - Endpoint Control Selectors UAC1
+typedef enum {
+ AUDIO10_EP_CTRL_UNDEF = 0x00,
+ AUDIO10_EP_CTRL_SAMPLING_FREQ = 0x01,
+ AUDIO10_EP_CTRL_PITCH = 0x02,
+} audio10_ep_control_selector_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC1
+typedef enum {
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ = 0x01,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_PITCH = 0x02,
+} audio10_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC1
+typedef enum {
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio10_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Feature Unit Controls UAC1
+typedef enum {
+ AUDIO10_FU_CONTROL_BM_MUTE = 1 << 0,
+ AUDIO10_FU_CONTROL_BM_VOLUME = 1 << 1,
+ AUDIO10_FU_CONTROL_BM_BASS = 1 << 2,
+ AUDIO10_FU_CONTROL_BM_MID = 1 << 3,
+ AUDIO10_FU_CONTROL_BM_TREBLE = 1 << 4,
+ AUDIO10_FU_CONTROL_BM_GRAPHIC_EQUALIZER = 1 << 5,
+ AUDIO10_FU_CONTROL_BM_AGC = 1 << 6,
+ AUDIO10_FU_CONTROL_BM_DELAY = 1 << 7,
+ AUDIO10_FU_CONTROL_BM_BASS_BOOST = 1 << 8,
+ AUDIO10_FU_CONTROL_BM_LOUDNESS = 1 << 9,
+} audio10_feature_unit_control_bitmap_t;
+
+/// A.1 - Audio Class-Format Type Codes UAC1
+typedef enum {
+ AUDIO10_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO10_FORMAT_TYPE_I = 0x01,
+ AUDIO10_FORMAT_TYPE_II = 0x02,
+ AUDIO10_FORMAT_TYPE_III = 0x03,
+} audio10_format_type_t;
+
+// A.1.1 - Audio Class-Audio Data Format Type I UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_I_PCM = 0x0001,
+ AUDIO10_DATA_FORMAT_TYPE_I_PCM8 = 0x0002,
+ AUDIO10_DATA_FORMAT_TYPE_I_IEEE_FLOAT = 0x0003,
+ AUDIO10_DATA_FORMAT_TYPE_I_ALAW = 0x0004,
+ AUDIO10_DATA_FORMAT_TYPE_I_MULAW = 0x0005,
+} audio10_data_format_type_I_t;
+
+// A.1.2 - Audio Class-Audio Data Format Type II UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_II_MPEG = 0x1001,
+ AUDIO10_DATA_FORMAT_TYPE_II_AC3 = 0x1002,
+} audio10_data_format_type_II_t;
+
+// A.1.3 - Audio Class-Audio Data Format Type III UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_AC3_1 = 0x2001,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG1_L1_1 = 0x2002,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG1_L23_1 = 0x2003,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_EXT_1 = 0x2004,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_L1_LS_1 = 0x2005,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_L23_LS_1 = 0x2006,
+} audio10_data_format_type_III_t;
+
+/// Audio Class-Audio Channel Configuration UAC1 (Table A-7)
+typedef enum {
+ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED = 0x0000,
+ AUDIO10_CHANNEL_CONFIG_LEFT_FRONT = 0x0001,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_FRONT = 0x0002,
+ AUDIO10_CHANNEL_CONFIG_CENTER_FRONT = 0x0004,
+ AUDIO10_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x0008,
+ AUDIO10_CHANNEL_CONFIG_LEFT_SURROUND = 0x0010,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_SURROUND = 0x0020,
+ AUDIO10_CHANNEL_CONFIG_LEFT_OF_CENTER = 0x0040,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_OF_CENTER = 0x0080,
+ AUDIO10_CHANNEL_CONFIG_SURROUND = 0x0100,
+ AUDIO10_CHANNEL_CONFIG_SIDE_LEFT = 0x0200,
+ AUDIO10_CHANNEL_CONFIG_SIDE_RIGHT = 0x0400,
+ AUDIO10_CHANNEL_CONFIG_TOP = 0x0800,
+} audio10_channel_config_t;
+
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 1.0 (UAC1) DESCRIPTORS
+//--------------------------------------------------------------------+
+
+/// AUDIO Class-Specific AC Interface Header Descriptor UAC1 (4.3.2)
+#define audio10_desc_cs_ac_interface_n_t(numInterfaces) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_HEADER. */ \
+ uint16_t bcdADC; /* Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: 0x0100 for UAC1. */ \
+ uint16_t wTotalLength; /* Total number of bytes returned for the class-specific AudioControl interface descriptor. */ \
+ uint8_t bInCollection; /* The number of AudioStreaming and MIDIStreaming interfaces in the Audio Interface Collection. */ \
+ uint8_t baInterfaceNr[numInterfaces]; /* Interface number of the AudioStreaming or MIDIStreaming interface in the Collection. */ \
+ }
+
+/// AUDIO Input Terminal Descriptor UAC1 (4.3.2.1)
+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: AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal.
+ uint8_t bAssocTerminal; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bNrChannels; ///< Number of logical output channels in the Terminal's output audio channel cluster.
+ uint16_t wChannelConfig; ///< Describes the spatial location of the logical channels.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first logical channel.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Input Terminal.
+} audio10_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor UAC1 (4.3.2.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: AUDIO10_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal.
+ 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 iTerminal; ///< Index of a string descriptor, describing the Output Terminal.
+} audio10_desc_output_terminal_t;
+
+/// AUDIO Mixer Unit Descriptor UAC1 (4.3.2.3)
+#define audio10_desc_mixer_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 10+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_MIXER_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Mixer Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Mixer Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bmControls[numControlBytes]; /* Mixer Unit Controls bitmap. */ \
+ uint8_t iMixer; /* Index of a string descriptor, describing the Mixer Unit. */ \
+ }
+
+/// AUDIO Selector Unit Descriptor UAC1 (4.3.2.4)
+#define audio10_desc_selector_unit_n_t(numInputPins) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 6+p. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_SELECTOR_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Selector Unit are connected. */ \
+ uint8_t iSelector; /* Index of a string descriptor, describing the Selector Unit. */ \
+ }
+
+/// AUDIO Feature Unit Descriptor UAC1 (4.3.2.5)
+#define audio10_desc_feature_unit_n_t(numChannels, controlSize) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 7+(ch+1)*n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bSourceID; /* ID of the Unit or Terminal to which this Feature Unit is connected. */ \
+ uint8_t bControlSize; /* Size in bytes of an element of the bmaControls() array. */ \
+ uint8_t bmaControls[(numChannels + 1) * controlSize]; /* Control bitmaps for master + logical channels. */ \
+ uint8_t iFeature; /* Index of a string descriptor, describing this Feature Unit. */ \
+ }
+
+/// AUDIO Processing Unit Descriptor UAC1 (4.3.2.6)
+#define audio10_desc_processing_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 13+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_PROCESSING_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint16_t wProcessType; /* Constant identifying the type of processing this Unit is performing. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Processing Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Processing Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bControlSize; /* Size in bytes of the bmControls field. */ \
+ uint8_t bmControls[numControlBytes]; /* Processing Unit Controls bitmap. */ \
+ uint8_t iProcessing; /* Index of a string descriptor, describing the Processing Unit. */ \
+ }
+
+/// AUDIO Extension Unit Descriptor UAC1 (4.3.2.7)
+#define audio10_desc_extension_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 13+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_EXTENSION_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint16_t wExtensionCode; /* Vendor-specific code identifying the Extension Unit. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Extension Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Extension Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bControlSize; /* Size in bytes of the bmControls field. */ \
+ uint8_t bmControls[numControlBytes]; /* Extension Unit Controls bitmap. */ \
+ uint8_t iExtension; /* Index of a string descriptor, describing the Extension Unit. */ \
+ }
+
+/// AUDIO Class-Specific AS Interface Descriptor UAC1 (4.5.2)
+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: AUDIO10_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink; ///< The Terminal ID of the Terminal to which the endpoint of this interface is connected.
+ uint8_t bDelay; ///< Expressed in number of frames.
+ uint16_t wFormatTag; ///< The Audio Data Format that has to be used to communicate with this interface.
+} audio10_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor UAC1 (2.2.5)
+#define audio10_desc_type_I_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint8_t bNrChannels; /* Indicates the number of physical channels in the audio data stream. */ \
+ uint8_t bSubFrameSize; /* The number of bytes occupied by one audio subframe. */ \
+ uint8_t bBitResolution; /* The number of effectively used bits from the available bits in an audio subframe. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// AUDIO Type II Format Type Descriptor UAC1 (2.3.5)
+#define audio10_desc_type_II_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 9+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint16_t wMaxBitRate; /* Indicates the maximum number of bits per second this interface can handle. */ \
+ uint16_t wSamplesPerFrame; /* Indicates the number of PCM audio samples contained in one encoded audio frame. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// AUDIO Type III Format Type Descriptor UAC1 (2.4.5)
+#define audio10_desc_type_III_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint8_t bNrChannels; /* Indicates the number of physical channels in the audio data stream. */ \
+ uint8_t bSubFrameSize; /* The number of bytes occupied by one audio subframe. */ \
+ uint8_t bBitResolution; /* The number of effectively used bits from the available bits in an audio subframe. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// Standard AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_ENDPOINT.
+ uint8_t bEndpointAddress;///< The address of the endpoint on the USB device described by this descriptor.
+ struct TU_ATTR_PACKED {
+#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE)
+ uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
+ uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
+ uint8_t usage : 2; // Data, Feedback, Implicit feedback
+ uint8_t : 2;
+#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE)
+ uint8_t : 2;
+ uint8_t usage : 2; // Data, Feedback, Implicit feedback
+ uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
+ uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
+#else
+ #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE"
+#endif
+ } bmAttributes;
+ uint16_t wMaxPacketSize; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.
+ uint8_t bInterval; ///< Interval for polling endpoint for data transfers.
+ uint8_t bRefresh; ///< The rate at which the endpoint is refreshed.
+ uint8_t bSynchAddress; ///< The address of the endpoint used to send synchronization information for the data endpoint.
+} audio10_desc_as_iso_data_ep_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 7.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO10_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes; ///< Bit 0: Sampling Frequency, Bit 1: Pitch, Bit 7: MaxPacketsOnly.
+ uint8_t bLockDelayUnits; ///< Indicates the units used for the wLockDelay field.
+ uint16_t wLockDelay; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry.
+} audio10_desc_cs_as_iso_data_ep_t;
+
+/// AUDIO Interrupt Data Message Format UAC1 (3.7.1.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bStatusType;///< Indicates the type of status information being reported.
+ uint8_t bOriginator;///< Indicates the entity that originated this status information.
+} audio10_interrupt_data_t;
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 2.0 (UAC2) DEFINITIONS
+//--------------------------------------------------------------------+
+
/// A.7 - Audio Function Category Codes
-typedef enum
-{
- AUDIO_FUNC_UNDEF = 0x00,
- AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
- AUDIO_FUNC_HOME_THEATER = 0x02,
- AUDIO_FUNC_MICROPHONE = 0x03,
- AUDIO_FUNC_HEADSET = 0x04,
- AUDIO_FUNC_TELEPHONE = 0x05,
- AUDIO_FUNC_CONVERTER = 0x06,
- AUDIO_FUNC_SOUND_RECODER = 0x07,
- AUDIO_FUNC_IO_BOX = 0x08,
- AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
- AUDIO_FUNC_PRO_AUDIO = 0x0A,
- AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
- AUDIO_FUNC_CONTROL_PANEL = 0x0C,
- AUDIO_FUNC_OTHER = 0xFF,
-} audio_function_code_t;
+typedef enum {
+ AUDIO20_FUNC_UNDEF = 0x00,
+ AUDIO20_FUNC_DESKTOP_SPEAKER = 0x01,
+ AUDIO20_FUNC_HOME_THEATER = 0x02,
+ AUDIO20_FUNC_MICROPHONE = 0x03,
+ AUDIO20_FUNC_HEADSET = 0x04,
+ AUDIO20_FUNC_TELEPHONE = 0x05,
+ AUDIO20_FUNC_CONVERTER = 0x06,
+ AUDIO20_FUNC_SOUND_RECODER = 0x07,
+ AUDIO20_FUNC_IO_BOX = 0x08,
+ AUDIO20_FUNC_MUSICAL_INSTRUMENT = 0x09,
+ AUDIO20_FUNC_PRO_AUDIO = 0x0A,
+ AUDIO20_FUNC_AUDIO_VIDEO = 0x0B,
+ AUDIO20_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO20_FUNC_OTHER = 0xFF,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO20_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO20_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO20_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO20_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO20_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO20_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO20_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO20_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO20_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO20_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO20_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO20_CS_AS_INTERFACE_DECODER = 0x04,
+} audio20_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;
+typedef enum {
+ AUDIO20_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO20_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO20_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO20_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO20_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio20_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;
+typedef enum {
+ AUDIO20_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO20_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO20_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO20_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO20_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio20_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.14 - Audio Class-Specific Request Codes UAC2
+typedef enum {
+ AUDIO20_CS_REQ_UNDEF = 0x00,
+ AUDIO20_CS_REQ_CUR = 0x01,
+ AUDIO20_CS_REQ_RANGE = 0x02,
+ AUDIO20_CS_REQ_MEM = 0x03,
+} audio20_cs_req_t;
-/// A.17 - Control Selector Codes
+/// A.17 - Control Selector Codes UAC2
/// 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;
+typedef enum {
+ AUDIO20_CS_CTRL_UNDEF = 0x00,
+ AUDIO20_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO20_CS_CTRL_CLK_VALID = 0x02,
+} audio20_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;
+typedef enum {
+ AUDIO20_CX_CTRL_UNDEF = 0x00,
+ AUDIO20_CX_CTRL_CONTROL = 0x01,
+} audio20_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;
+typedef enum {
+ AUDIO20_CM_CTRL_UNDEF = 0x00,
+ AUDIO20_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO20_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio20_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.4 - Terminal Control Selectors UAC2
+typedef enum {
+ AUDIO20_TE_CTRL_UNDEF = 0x00,
+ AUDIO20_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO20_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO20_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO20_TE_CTRL_CLUSTER = 0x04,
+ AUDIO20_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO20_TE_CTRL_LATENCY = 0x07,
+} audio20_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;
+typedef enum {
+ AUDIO20_MU_CTRL_UNDEF = 0x00,
+ AUDIO20_MU_CTRL_MIXER = 0x01,
+ AUDIO20_MU_CTRL_CLUSTER = 0x02,
+ AUDIO20_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO20_MU_CTRL_LATENCY = 0x05,
+} audio20_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;
+typedef enum {
+ AUDIO20_SU_CTRL_UNDEF = 0x00,
+ AUDIO20_SU_CTRL_SELECTOR = 0x01,
+ AUDIO20_SU_CTRL_LATENCY = 0x02,
+} audio20_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.7 - Feature Unit Control Selectors UAC2
+typedef enum {
+ AUDIO20_FU_CTRL_UNDEF = 0x00,
+ AUDIO20_FU_CTRL_MUTE = 0x01,
+ AUDIO20_FU_CTRL_VOLUME = 0x02,
+ AUDIO20_FU_CTRL_BASS = 0x03,
+ AUDIO20_FU_CTRL_MID = 0x04,
+ AUDIO20_FU_CTRL_TREBLE = 0x05,
+ AUDIO20_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO20_FU_CTRL_AGC = 0x07,
+ AUDIO20_FU_CTRL_DELAY = 0x08,
+ AUDIO20_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO20_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO20_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO20_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO20_FU_CTRL_INVERTER = 0x0D,
+ AUDIO20_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO20_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO20_FU_CTRL_LATENCY = 0x10,
+} audio20_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;
+typedef enum {
+ AUDIO20_PE_CTRL_UNDEF = 0x00,
+ AUDIO20_PE_CTRL_ENABLE = 0x01,
+ AUDIO20_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO20_PE_CTRL_QFACTOR = 0x03,
+ AUDIO20_PE_CTRL_GAIN = 0x04,
+ AUDIO20_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO20_PE_CTRL_LATENCY = 0x07,
+} audio20_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;
+typedef enum {
+ AUDIO20_RV_CTRL_UNDEF = 0x00,
+ AUDIO20_RV_CTRL_ENABLE = 0x01,
+ AUDIO20_RV_CTRL_TYPE = 0x02,
+ AUDIO20_RV_CTRL_LEVEL = 0x03,
+ AUDIO20_RV_CTRL_TIME = 0x04,
+ AUDIO20_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO20_RV_CTRL_PREDELAY = 0x06,
+ AUDIO20_RV_CTRL_DENSITY = 0x07,
+ AUDIO20_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO20_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO20_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO20_RV_CTRL_LATENCY = 0x0B,
+} audio20_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;
+typedef enum {
+ AUDIO20_MD_CTRL_UNDEF = 0x00,
+ AUDIO20_MD_CTRL_ENABLE = 0x01,
+ AUDIO20_MD_CTRL_BALANCE = 0x02,
+ AUDIO20_MD_CTRL_RATE = 0x03,
+ AUDIO20_MD_CTRL_DEPTH = 0x04,
+ AUDIO20_MD_CTRL_TIME = 0x05,
+ AUDIO20_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO20_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO20_MD_CTRL_LATENCY = 0x09,
+} audio20_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;
+typedef enum {
+ AUDIO20_DR_CTRL_UNDEF = 0x00,
+ AUDIO20_DR_CTRL_ENABLE = 0x01,
+ AUDIO20_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO20_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO20_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO20_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO20_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO20_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO20_DR_CTRL_LATENCY = 0x09,
+} audio20_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;
+typedef enum {
+ AUDIO20_UD_CTRL_UNDEF = 0x00,
+ AUDIO20_UD_CTRL_ENABLE = 0x01,
+ AUDIO20_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO20_UD_CTRL_CLUSTER = 0x03,
+ AUDIO20_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO20_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO20_UD_CTRL_LATENCY = 0x06,
+} audio20_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;
+typedef enum {
+ AUDIO20_DP_CTRL_UNDEF = 0x00,
+ AUDIO20_DP_CTRL_ENABLE = 0x01,
+ AUDIO20_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO20_DP_CTRL_CLUSTER = 0x03,
+ AUDIO20_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO20_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO20_DP_CTRL_LATENCY = 0x06,
+} audio20_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;
+typedef enum {
+ AUDIO20_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO20_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO20_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO20_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO20_ST_EXT_CTRL_LATENCY = 0x05,
+} audio20_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;
+typedef enum {
+ AUDIO20_XU_CTRL_UNDEF = 0x00,
+ AUDIO20_XU_CTRL_ENABLE = 0x01,
+ AUDIO20_XU_CTRL_CLUSTER = 0x02,
+ AUDIO20_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO20_XU_CTRL_LATENCY = 0x05,
+} audio20_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;
+typedef enum {
+ AUDIO20_AS_CTRL_UNDEF = 0x00,
+ AUDIO20_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO20_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO20_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio20_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;
+typedef enum {
+ AUDIO20_EN_CTRL_UNDEF = 0x00,
+ AUDIO20_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO20_EN_CTRL_QUALITY = 0x02,
+ AUDIO20_EN_CTRL_VBR = 0x03,
+ AUDIO20_EN_CTRL_TYPE = 0x04,
+ AUDIO20_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO20_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO20_EN_CTRL_PARAM1 = 0x08,
+ AUDIO20_EN_CTRL_PARAM2 = 0x09,
+ AUDIO20_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO20_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO20_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO20_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO20_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO20_EN_CTRL_PARAM8 = 0x0F,
+} audio20_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;
+typedef enum {
+ AUDIO20_MPD_CTRL_UNDEF = 0x00,
+ AUDIO20_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO20_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO20_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO20_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO20_MPD_CTRL_SCALING = 0x05,
+ AUDIO20_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO20_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO20_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio20_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;
+typedef enum {
+ AUDIO20_AD_CTRL_UNDEF = 0x00,
+ AUDIO20_AD_CTRL_MODE = 0x01,
+ AUDIO20_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO20_AD_CTRL_SCALING = 0x03,
+ AUDIO20_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO20_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO20_AD_CTRL_DECODER_ERROR = 0x07,
+} audio20_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;
+typedef enum {
+ AUDIO20_WD_CTRL_UNDEF = 0x00,
+ AUDIO20_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO20_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO20_WD_CTRL_DECODER_ERROR = 0x03,
+} audio20_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;
+typedef enum {
+ AUDIO20_DD_CTRL_UNDEF = 0x00,
+ AUDIO20_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO20_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO20_DD_CTRL_DECODER_ERROR = 0x03,
+} audio20_DTS_decoder_control_selector_t;
/// A.17.14 Endpoint Control Selectors
-typedef enum
-{
- AUDIO_EP_CTRL_UNDEF = 0x00,
- AUDIO_EP_CTRL_PITCH = 0x01,
- AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
- AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
-} audio_EP_control_selector_t;
-
-/// Terminal Types
-
-/// 2.1 - Audio Class-Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
- AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
- AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
-} audio_terminal_type_t;
-
-/// 2.2 - Audio Class-Input Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
- AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
- AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
- AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
- AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
- AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
- AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
-} audio_terminal_input_type_t;
-
-/// 2.3 - Audio Class-Output Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
- AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
- AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
- AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
- AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
- AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
- AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
- AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
-} audio_terminal_output_type_t;
-
-/// Rest is yet to be implemented
+typedef enum {
+ AUDIO20_EP_CTRL_UNDEF = 0x00,
+ AUDIO20_EP_CTRL_PITCH = 0x01,
+ AUDIO20_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO20_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio20_EP_control_selector_t;
/// 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;
+typedef enum {
+ AUDIO20_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO20_FORMAT_TYPE_I = 0x01,
+ AUDIO20_FORMAT_TYPE_II = 0x02,
+ AUDIO20_FORMAT_TYPE_III = 0x03,
+ AUDIO20_FORMAT_TYPE_IV = 0x04,
+ AUDIO20_EXT_FORMAT_TYPE_I = 0x81,
+ AUDIO20_EXT_FORMAT_TYPE_II = 0x82,
+ AUDIO20_EXT_FORMAT_TYPE_III = 0x83,
+} audio20_format_type_t;
// A.2.1 - Audio Class-Audio Data Format Type I UAC2
-typedef enum
-{
- AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
- AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
- AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
- AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
- AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
- AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
-} audio_data_format_type_I_t;
+typedef enum {
+ AUDIO20_DATA_FORMAT_TYPE_I_PCM = 1 << 0,
+ AUDIO20_DATA_FORMAT_TYPE_I_PCM8 = 1 << 1,
+ AUDIO20_DATA_FORMAT_TYPE_I_IEEE_FLOAT = 1 << 2,
+ AUDIO20_DATA_FORMAT_TYPE_I_ALAW = 1 << 3,
+ AUDIO20_DATA_FORMAT_TYPE_I_MULAW = 1 << 4,
+ AUDIO20_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
+} audio20_data_format_type_I_t;
+
+/// Audio Class-Audio Channel Configuration UAC2 (Table A-11)
+typedef enum {
+ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO20_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO20_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO20_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO20_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO20_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO20_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO20_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO20_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO20_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO20_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO20_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO20_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO20_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO20_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO20_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO20_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO20_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO20_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO20_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO20_CHANNEL_CONFIG_RAW_DATA = 0x80000000u,
+} audio20_channel_config_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
/// 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;
+typedef enum {
+ AUDIO20_CTRL_NONE = 0x00,///< No Host access
+ AUDIO20_CTRL_R = 0x01, ///< Host read access only
+ AUDIO20_CTRL_RW = 0x03, ///< Host read write access
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO20_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio20_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;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio20_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;
+typedef enum {
+ AUDIO20_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO20_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio20_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;
+typedef enum {
+ AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO20_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio20_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;
+typedef enum {
+ AUDIO20_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio20_clock_selector_control_pos_t;
/// Audio Class-Clock Multiplier Controls UAC2
-typedef enum
-{
- AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
- AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
-} audio_clock_multiplier_control_pos_t;
+typedef enum {
+ AUDIO20_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO20_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio20_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;
+typedef enum {
+ AUDIO20_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO20_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO20_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO20_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO20_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO20_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio20_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;
+typedef enum {
+ AUDIO20_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO20_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO20_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO20_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO20_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio20_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;
+typedef enum {
+ AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO20_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO20_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO20_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO20_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO20_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO20_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO20_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO20_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO20_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO20_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO20_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO20_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO20_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio20_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 = 0x80000000u,
-} audio_channel_config_t;
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 2.0 (UAC2) DESCRIPTORS
+//--------------------------------------------------------------------+
-/// AUDIO Channel Cluster Descriptor (4.1)
+/// AUDIO Channel Cluster Descriptor UAC2 (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;
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ uint32_t bmChannelConfig;///< Bitmap according to 'audio20_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.
+} audio20_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;
-TU_VERIFY_STATIC(sizeof(audio_desc_cs_ac_interface_t) == 9, "size is not correct");
+/// AUDIO Class-Specific AC Interface Header Descriptor UAC2 (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: AUDIO20_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: audio20_function_code_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: audio20_cs_ac_interface_control_pos_t.
+} audio20_desc_cs_ac_interface_t;
+TU_VERIFY_STATIC(sizeof(audio20_desc_cs_ac_interface_t) == 9, "size is not correct");
-/// 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 Source Descriptor UAC2 (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: AUDIO20_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: audio20_clock_source_attribute_t.
+ uint8_t bmControls; ///< See: audio20_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.
+} audio20_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 UAC2 (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: AUDIO20_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: audio20_clock_selector_control_pos_t.
+ uint8_t iClockSource; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio20_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 ; \
-}
+#define audio20_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 Clock Multiplier Descriptor UAC2 (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: AUDIO20_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: audio20_clock_multiplier_control_pos_t.
+ uint8_t iClockSource; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio20_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.
- 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_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.
- uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first logical channel.
- 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;
+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.
+ 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_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:audio20_channel_config_t.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first logical channel.
+ uint16_t bmControls; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Input Terminal.
+} audio20_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 Output Terminal Descriptor UAC2 (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: AUDIO20_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: audio20_terminal_type_t for USB streaming and audio20_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: audio20_terminal_output_control_pos_t.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Output Terminal.
+} audio20_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.
+/// AUDIO Feature Unit Descriptor UAC2 (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: AUDIO20_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;
+ uint32_t bmaControls;///< See: audio20_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.
+} audio20_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 ; \
-}
+#define audio20_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;
+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: AUDIO20_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls; ///< See: audio20_cs_as_interface_control_pos_t.
+ uint8_t bFormatType; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio20_format_type_t.
+ uint32_t bmFormats; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio20_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: audio20_channel_config_t.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio20_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;
+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: AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO20_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.
+} audio20_desc_type_I_format_t;
/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
- uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
- uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
- uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
- uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
-} audio_desc_cs_as_iso_data_ep_t;
-
-// 5.2.2 Control Request Layout
-typedef struct TU_ATTR_PACKED
-{
- union
- {
- struct TU_ATTR_PACKED
- {
- uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
- uint8_t type : 2; ///< Request type tusb_request_type_t.
- uint8_t direction : 1; ///< Direction type. tusb_dir_t
- } bmRequestType_bit;
-
- uint8_t bmRequestType;
- };
-
- uint8_t bRequest; ///< Request type audio_cs_req_t
- uint8_t bChannelNumber;
- uint8_t bControlSelector;
- union
- {
- uint8_t bInterface;
- uint8_t bEndpoint;
- };
- uint8_t bEntityID;
- uint16_t wLength;
-} audio_control_request_t;
+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: AUDIO20_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes; ///< See: audio20_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls; ///< See: audio20_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits; ///< Indicates the units used for the wLockDelay field. See: audio20_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.
+} audio20_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;
+typedef struct TU_ATTR_PACKED {
+ int8_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_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;
+ int16_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_control_cur_2_t;
-// 5.2.3.2 2-byte Control RANGE Parameter Block
+// 5.2.3.3 4-byte Control CUR 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;
+ int32_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_control_cur_4_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;
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the #define macros below for predefined lengths.
// 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] ; \
-}
+#define audio20_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]; \
-}
+#define audio20_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]; \
-}
+#define audio20_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]; \
+ }
// 6.1 Interrupt Data Message Format
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bInfo;
uint8_t bAttribute;
- union
- {
+ union {
uint16_t wValue;
- struct
- {
+ struct {
uint8_t wValue_cn_or_mcn;
uint8_t wValue_cs;
};
};
- union
- {
+ union {
uint16_t wIndex;
- struct
- {
+ struct {
uint8_t wIndex_ep_or_int;
uint8_t wIndex_entity_id;
};
};
+} audio20_interrupt_data_t;
+
+//--------------------------------------------------------------------+
+// APPLICATION HELPER DEFINITIONS
+//--------------------------------------------------------------------+
+
+// Combined Interrupt Data Message Format for both UAC1 and UAC2
+typedef union {
+ audio10_interrupt_data_t v1;
+ audio20_interrupt_data_t v2;
} audio_interrupt_data_t;
+// MIDI1.0 use the same CS AC Interface Descriptor as UAC1
+typedef audio10_desc_cs_ac_interface_n_t(1) midi10_desc_cs_ac_interface_t;
+
+// UAC1.0 AC Interface Descriptor with 1 interface, used to read fields other than baInterfaceNr
+typedef audio10_desc_cs_ac_interface_n_t(1) audio10_desc_cs_ac_interface_1_t;
+
/** @} */
#ifdef __cplusplus
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 701411401..995bf8a3e 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -65,41 +65,6 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-// Use ring buffer if it's available, some MCUs need extra RAM requirements
-// For DWC2 enable ring buffer will disable DMA (if available)
-#ifndef TUD_AUDIO_PREFER_RING_BUFFER
- #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
- defined(TUP_USBIP_DWC2)
- #define TUD_AUDIO_PREFER_RING_BUFFER 0
- #else
- #define TUD_AUDIO_PREFER_RING_BUFFER 1
- #endif
-#endif
-
-// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
-// is available or driver is would need to be changed dramatically
-
-// Only STM32 and dcd_transdimension use non-linear buffer for now
-// dwc2 except esp32sx (since it may use dcd_esp32sx)
-// Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line
-#if (defined(TUP_USBIP_DWC2) && !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)) || \
- defined(TUP_USBIP_FSDEV) || \
- CFG_TUSB_MCU == OPT_MCU_RX63X || \
- CFG_TUSB_MCU == OPT_MCU_RX65X || \
- CFG_TUSB_MCU == OPT_MCU_RX72N || \
- CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
- CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
- CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
- CFG_TUSB_MCU == OPT_MCU_MSP432E4
- #if TUD_AUDIO_PREFER_RING_BUFFER && !CFG_TUD_MEM_DCACHE_ENABLE
- #define USE_LINEAR_BUFFER 0
- #else
- #define USE_LINEAR_BUFFER 1
- #endif
-#else
- #define USE_LINEAR_BUFFER 1
-#endif
-
// Declaration of buffers
// Check for maximum supported numbers
@@ -108,12 +73,12 @@
#endif
// Put swap buffer in USB section only if necessary
-#if USE_LINEAR_BUFFER
+#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-#if USE_LINEAR_BUFFER
+#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
@@ -136,7 +101,7 @@ tu_static IN_SW_BUF_MEM_ATTR struct {
// Linear buffer TX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX);
@@ -148,7 +113,7 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX);
#endif
} lin_buf_in;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
// EP OUT software buffers
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -167,7 +132,7 @@ tu_static OUT_SW_BUF_MEM_ATTR struct {
// Linear buffer RX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX);
@@ -179,18 +144,12 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX);
#endif
} lin_buf_out;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
-// Control buffers
-tu_static CFG_TUD_MEM_SECTION struct {
- TUD_EPBUF_DEF(buf1, CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ);
- #if CFG_TUD_AUDIO > 1
- TUD_EPBUF_DEF(buf2, CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ);
- #endif
- #if CFG_TUD_AUDIO > 2
- TUD_EPBUF_DEF(buf3, CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ);
- #endif
-} ctrl_buf;
+// Control buffer
+#if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+tu_static CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
+#endif
// Aligned buffer for feedback EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -218,12 +177,15 @@ typedef struct
{
uint8_t rhport;
uint8_t const *p_desc;// Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+ uint8_t const *p_desc_as;// Pointer pointing to 1st Standard AS Interface Descriptor(4.9.1) - Audio Streaming descriptor defining audio function
+ uint16_t desc_length;// Length of audio function descriptor
#if CFG_TUD_AUDIO_ENABLE_EP_IN
uint8_t ep_in; // TX audio data EP.
uint16_t ep_in_sz; // Current size of TX EP
uint8_t ep_in_as_intf_num;// Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
uint8_t ep_in_alt; // Current alternate setting of TX EP
+ uint16_t ep_in_fifo_threshold;// Target size for the EP IN FIFO.
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -243,8 +205,6 @@ typedef struct
bool mounted;// Device opened
- uint16_t desc_length;// Length of audio function descriptor
-
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
struct {
uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
@@ -253,7 +213,6 @@ typedef struct
uint8_t frame_shift;// bInterval-1 in unit of frame (FS), micro-frame (HS)
uint8_t compute_method;
- bool format_correction;
union {
uint8_t power_of_2;// pre-computed power of 2 shift
float float_const; // pre-computed float constant
@@ -279,21 +238,13 @@ typedef struct
uint16_t packet_sz_tx[3];
uint8_t bclock_id_tx;
uint8_t interval_tx;
-#endif
-
-// Encoding parameters - parameters are set when alternate AS interface is set by host
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- audio_format_type_t format_type_tx;
+ uint8_t format_type_tx;
uint8_t n_channels_tx;
uint8_t n_bytes_per_sample_tx;
#endif
/*------------- From this point, data is not cleared by bus reset -------------*/
- // Buffer for control requests
- uint8_t *ctrl_buf;
- uint8_t ctrl_buf_sz;
-
// EP Transfer buffers and FIFOs
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_fifo_t ep_out_ff;
@@ -304,14 +255,12 @@ typedef struct
#endif
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_out;
- #define USE_LINEAR_BUFFER_RX 1
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_in;
- #define USE_LINEAR_BUFFER_TX 1
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -319,16 +268,12 @@ typedef struct
#endif
} audiod_function_t;
-#ifndef USE_LINEAR_BUFFER_TX
- #define USE_LINEAR_BUFFER_TX 0
-#endif
-
-#ifndef USE_LINEAR_BUFFER_RX
- #define USE_LINEAR_BUFFER_RX 0
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_out_ff)
+#else
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_in_ff)
#endif
-#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
-
//--------------------------------------------------------------------+
// WEAK FUNCTION STUBS
//--------------------------------------------------------------------+
@@ -363,11 +308,6 @@ TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf,
feedback_param->method = AUDIO_FEEDBACK_METHOD_DISABLED;
}
-TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
- (void) func_id;
- return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION;
-}
-
TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) {
(void) func_id;
(void) frame_number;
@@ -376,7 +316,7 @@ TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
-TU_ATTR_WEAK void tud_audio_int_xfer_cb(uint8_t rhport) {
+TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) {
(void) rhport;
}
#endif
@@ -465,16 +405,16 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id);
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc);
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio);
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_size);
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_size);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq);
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt);
static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_new);
#endif
@@ -485,6 +425,15 @@ bool tud_audio_n_mounted(uint8_t func_id) {
return audio->mounted;
}
+static inline uint8_t* get_ctrl_buffer(void) {
+ // Use EP0 buffer if it is large enough, otherwise use dedicated buffer
+ #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+ return ctrl_buf;
+ #else
+ return usbd_get_ctrl_buf();
+ #endif
+}
+
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
@@ -503,7 +452,8 @@ uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize) {
bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
+ tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
+ return true;
}
tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
@@ -516,15 +466,15 @@ tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) {
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
- #if USE_LINEAR_BUFFER_RX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Data currently is in linear buffer, copy into EP OUT FIFO
- TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
+ TU_VERIFY(0 < tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
// Schedule for next receive
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz, true));
#else
// Data is already placed in EP FIFO, schedule for next receive
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz, true));
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -554,7 +504,8 @@ uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
+ tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
+ return true;
}
tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
@@ -564,6 +515,17 @@ tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
return NULL;
}
+uint16_t tud_audio_n_get_ep_in_fifo_threshold(uint8_t func_id) {
+ if (func_id < CFG_TUD_AUDIO) return _audiod_fct[func_id].ep_in_fifo_threshold;
+ return 0;
+}
+
+void tud_audio_n_set_ep_in_fifo_threshold(uint8_t func_id, uint16_t threshold) {
+ if (func_id < CFG_TUD_AUDIO && threshold < _audiod_fct[func_id].ep_in_ff.depth) {
+ _audiod_fct[func_id].ep_in_fifo_threshold = threshold;
+ }
+}
+
static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16_t n_bytes_sent) {
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
@@ -575,16 +537,16 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
// packet_sz_tx is based on total packet size, here we want size for each support buffer.
- n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
+ n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_fifo_threshold, audio->ep_in_sz);
#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
#endif
- #if USE_LINEAR_BUFFER_TX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx, true));
#else
// Send everything in ISO EP FIFO
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx, true));
#endif
// Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer
@@ -595,6 +557,10 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16
#endif
+//--------------------------------------------------------------------+
+// OTHER API
+//--------------------------------------------------------------------+
+
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_done_cb() is called in inform user
bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data) {
@@ -605,10 +571,15 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
// We write directly into the EP's buffer - abort if previous transfer not complete
TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int));
+ uint8_t size = tud_audio_n_version(func_id) == 2 ? sizeof(audio20_interrupt_data_t) : sizeof(audio10_interrupt_data_t);
+
+ // INT EP buffer must be large enough
+ TU_ASSERT(size <= sizeof(int_ep_buf[func_id].buf));
+
// Check length
- if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, sizeof(audio_interrupt_data_t)) == 0) {
+ if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, size) == 0) {
// Schedule transmit
- TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf)), 0);
+ TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, size, false));
} else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int);
@@ -620,10 +591,11 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
-static inline bool audiod_fb_send(audiod_function_t *audio) {
- bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) && audio->feedback.format_correction;
+static inline bool audiod_fb_send(uint8_t func_id, bool is_isr) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint8_t uac_version = tud_audio_n_version(func_id);
// Format the feedback value
- if (apply_correction) {
+ if (uac_version == 1) {
uint8_t *fb = (uint8_t *) audio->fb_buf;
// For FS format is 10.14
@@ -635,23 +607,70 @@ static inline bool audiod_fb_send(audiod_function_t *audio) {
*audio->fb_buf = audio->feedback.value;
}
- // About feedback format on FS
- //
- // 3 variables: Format | packetSize | sendSize | Working OS:
- // 16.16 4 4 Linux, Windows
- // 16.16 4 3 Linux
- // 16.16 3 4 Linux
- // 16.16 3 3 Linux
- // 10.14 4 4 Linux
- // 10.14 4 3 Linux
- // 10.14 3 4 Linux, OSX
- // 10.14 3 3 Linux, OSX
- //
- // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty ugly
- return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, apply_correction ? 3 : 4);
+ return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, uac_version == 1 ? 3 : 4, is_isr);
+}
+
+uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint32_t feedback;
+
+ switch (audio->feedback.compute_method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
+ feedback = (cycles << audio->feedback.compute.power_of_2);
+ break;
+
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
+ break;
+
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
+ uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
+ feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
+ } break;
+
+ default:
+ return 0;
+ }
+
+ // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
+ // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
+ // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
+ // (audio slot = channel count samples).
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
+ }
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
+ }
+
+ tud_audio_n_fb_set(func_id, feedback);
+
+ return feedback;
+}
+
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ _audiod_fct[func_id].feedback.value = feedback;
+
+ return true;
}
#endif
+uint8_t tud_audio_n_version(uint8_t func_id) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ uint8_t bIntfProtocol = ((tusb_desc_interface_t const *)_audiod_fct[func_id].p_desc)->bInterfaceProtocol;
+
+ if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V1) {
+ return 1;
+ } else if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V2) {
+ return 2;
+ } else {
+ return 0; // Unknown version
+ }
+}
+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
@@ -661,111 +680,89 @@ void audiod_init(void) {
for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
audiod_function_t *audio = &_audiod_fct[i];
- // Initialize control buffers
- switch (i) {
- case 0:
- audio->ctrl_buf = ctrl_buf.buf1;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
- break;
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
- case 1:
- audio->ctrl_buf = ctrl_buf.buf2;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
- case 2:
- audio->ctrl_buf = ctrl_buf.buf3;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
- break;
-#endif
- }
-
// Initialize IN EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, true);
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
- // Initialize linear buffers
-#if USE_LINEAR_BUFFER_TX
+ // Initialize linear buffers
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
case 0:
audio->lin_buf_in = lin_buf_in.buf_1;
break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
case 1:
audio->lin_buf_in = lin_buf_in.buf_2;
break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
case 2:
audio->lin_buf_in = lin_buf_in.buf_3;
break;
- #endif
+ #endif
}
-#endif// USE_LINEAR_BUFFER_TX
+ #endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Initialize OUT EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, true);
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
- // Initialize linear buffers
-#if USE_LINEAR_BUFFER_RX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ // Initialize linear buffers
switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
case 0:
audio->lin_buf_out = lin_buf_out.buf_1;
break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
case 1:
audio->lin_buf_out = lin_buf_out.buf_2;
break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
case 2:
audio->lin_buf_out = lin_buf_out.buf_3;
break;
- #endif
+ #endif
}
-#endif// USE_LINEAR_BUFFER_RX
+ #endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
switch (i) {
@@ -814,19 +811,44 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
(void) max_len;
TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
- AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass, 0);
// Verify version is correct - this check can be omitted
- TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V1 ||
+ itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2, 0);
+
+ // Verify 2nd interface descriptor is Audio Streaming to avoid mess with MIDI class
+ // Audio Control interface is followed by Audio Streaming interface(s)
+ // MIDI class also starts with Audio Control but is followed by MIDI Streaming
+ {
+ uint8_t const *p_desc = (uint8_t const *) itf_desc;
+ uint8_t const *p_desc_end = p_desc + max_len;
+
+ // Advance to next interface descriptor
+ p_desc = tu_desc_next(p_desc);
+ while (tu_desc_in_bounds(p_desc, p_desc_end) && tu_desc_type(p_desc) != TUSB_DESC_INTERFACE) {
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Verify next interface is Audio Streaming (subclass 2), not MIDI Streaming (subclass 3)
+ if (p_desc_end - p_desc >= (int)sizeof(tusb_desc_interface_t)) {
+ tusb_desc_interface_t const *next_itf = (tusb_desc_interface_t const *) p_desc;
+ TU_VERIFY(next_itf->bInterfaceClass == TUSB_CLASS_AUDIO &&
+ next_itf->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING, 0);
+ } else {
+ // No further interface found or not enough bytes for interface descriptor
+ return 0;
+ }
+ }
// Verify interrupt control EP is enabled if demanded by descriptor
- TU_ASSERT(itf_desc->bNumEndpoints <= 1);// 0 or 1 EPs are allowed
+ TU_ASSERT(itf_desc->bNumEndpoints <= 1, 0);// 0 or 1 EPs are allowed
if (itf_desc->bNumEndpoints == 1) {
- TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP);
+ TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP, 0);
}
// Alternate setting MUST be zero - this check can be omitted
- TU_VERIFY(itf_desc->bAlternateSetting == 0);
+ TU_VERIFY(itf_desc->bAlternateSetting == 0, 0);
// Find available audio driver interface
uint8_t i;
@@ -835,21 +857,32 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
_audiod_fct[i].p_desc = (uint8_t const *) itf_desc;// Save pointer to AC descriptor which is by specification always the first one
_audiod_fct[i].rhport = rhport;
- // Setup descriptor lengths
- switch (i) {
- case 0:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
- break;
-#if CFG_TUD_AUDIO > 1
- case 1:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_2_DESC_LEN;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2
- case 2:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_3_DESC_LEN;
- break;
-#endif
+ // Calculate descriptor length
+ {
+ uint8_t const *p_desc = (uint8_t const *) itf_desc;
+ uint8_t const *p_desc_end = p_desc + max_len;
+ uint16_t total_len = sizeof(tusb_desc_interface_t);
+ // Skip Standard AC interface descriptor
+ p_desc = tu_desc_next(p_desc);
+ while (p_desc_end - p_desc > 0) {
+ // Stop if:
+ // - Non audio streaming interface descriptor found
+ // - IAD found
+ if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
+ !(((tusb_desc_interface_t const *) p_desc)->bInterfaceClass == TUSB_CLASS_AUDIO && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING))
+ || tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
+ break;
+ } else if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING) {
+ if (_audiod_fct[i].p_desc_as == NULL) {
+ _audiod_fct[i].p_desc_as = p_desc;
+ }
+ } else {
+ // nothing to do
+ }
+ total_len += p_desc[0];
+ p_desc = tu_desc_next(p_desc);
+ }
+ _audiod_fct[i].desc_length = total_len;
}
#ifdef TUP_DCD_EDPT_ISO_ALLOC
@@ -868,54 +901,67 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
uint8_t ep_fb = 0;
#endif
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ // Unified UAC1/UAC2 endpoint processing
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Explicit feedback EP
- if (desc_ep->bmAttributes.usage == 1) {
- ep_fb = desc_ep->bEndpointAddress;
- }
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_IN
- // Data or data with implicit feedback IN EP
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN
- && (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2)) {
- ep_in = desc_ep->bEndpointAddress;
- ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
- }
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_OUT
- // Data OUT EP
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT
- && desc_ep->bmAttributes.usage == 0) {
- ep_out = desc_ep->bEndpointAddress;
- ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
- }
- #endif
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(i) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
}
+
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Explicit feedback EP
+ if (is_feedback_ep) {
+ ep_fb = desc_ep->bEndpointAddress;
+ }
+ #else
+ (void) is_feedback_ep;
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ // Data or data with implicit feedback IN EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN && is_data_ep) {
+ ep_in = desc_ep->bEndpointAddress;
+ ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
+ }
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ // Data OUT EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT && is_data_ep) {
+ ep_out = desc_ep->bEndpointAddress;
+ ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
+ }
+ #endif
}
p_desc = tu_desc_next(p_desc);
}
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (ep_in) {
+ if (ep_in != 0) {
usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (ep_out) {
+ if (ep_out != 0) {
usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (ep_fb) {
+ if (ep_fb != 0) {
usbd_edpt_iso_alloc(rhport, ep_fb, 4);
}
#endif
@@ -925,22 +971,27 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
// For data or data with implicit feedback IN EP
+ // For UAC1 this is always the case since there is no usage field
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN
&& (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2)) {
_audiod_fct[i].interval_tx = desc_ep->bInterval;
}
}
- } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ } else if (tud_audio_n_version(i) == 2 &&
+ tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ // For UAC2 only, UAC1 doesn't have a clock source
if (tu_unaligned_read16(p_desc + 4) == AUDIO_TERM_TYPE_USB_STREAMING) {
_audiod_fct[i].bclock_id_tx = p_desc[8];
}
+ } else {
+ // nothing to do
}
p_desc = tu_desc_next(p_desc);
}
@@ -950,7 +1001,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
// For each endpoint
@@ -978,7 +1029,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
TU_ASSERT(i < CFG_TUD_AUDIO);
// This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
- uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;// - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+ uint16_t drv_len = _audiod_fct[i].desc_length;
return drv_len;
}
@@ -1088,11 +1139,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
- uint8_t const *p_desc = tu_desc_next(audio->p_desc);
- // Skip entire AC descriptor block
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
+
+ uint8_t const *p_desc = audio->p_desc_as;
// Get pointer at end
- uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length;
// p_desc starts at required interface with alternate setting zero
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
@@ -1115,36 +1165,52 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#endif
uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(func_id) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
+ }
+
//TODO: We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
usbd_edpt_clear_stall(rhport, ep_addr);
#if CFG_TUD_AUDIO_ENABLE_EP_IN
// For data or data with implicit feedback IN EP
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2))
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && is_data_ep)
{
// Save address
audio->ep_in = ep_addr;
audio->ep_in_as_intf_num = itf;
audio->ep_in_alt = alt;
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
+ // Set the default EP IN FIFO threshold to half fifo depth.
+ audio->ep_in_fifo_threshold = audio->ep_in_ff.depth / 2;
// If flow control is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_flow_control_params(audio, p_desc_parse_for_params);
#endif
// Schedule first transmit if alternate interface is not zero, as sample data is available a ZLP is loaded
- #if USE_LINEAR_BUFFER_TX
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0));
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0, false));
#else
// Send everything in ISO EP FIFO
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0));
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0, false));
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// Checking usage not necessary
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT && is_data_ep) {
// Save address
audio->ep_out = ep_addr;
audio->ep_out_as_intf_num = itf;
@@ -1152,22 +1218,26 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
// Prepare for incoming data
- #if USE_LINEAR_BUFFER_RX
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz, false));
#else
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz, false));
#endif
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Check if usage is explicit data feedback
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) {
+ if (is_feedback_ep) {
audio->ep_fb = ep_addr;
audio->feedback.frame_shift = desc_ep->bInterval - 1;
// Schedule first feedback transmit
- audiod_fb_send(audio);
+ audiod_fb_send(func_id, false);
}
+ #else
+ (void) is_feedback_ep;
#endif
+#else
+ (void) is_feedback_ep;
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
foundEPs += 1;
@@ -1181,50 +1251,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Prepare feedback computation if endpoint is available
- if (audio->ep_fb != 0) {
- audio_feedback_params_t fb_param;
-
- tud_audio_feedback_params_cb(func_id, alt, &fb_param);
- audio->feedback.compute_method = fb_param.method;
-
- if (TUSB_SPEED_FULL == tud_speed_get())
- audio->feedback.format_correction = tud_audio_feedback_format_correction_cb(func_id);
-
- // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
- uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
- audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
- audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
-
- switch (fb_param.method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
- // Initialize the threshold level to half filled
- uint16_t fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
- audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
- audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_lvl_thr) << 16;
- // Avoid 64bit division
- uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
- audio->feedback.compute.fifo_count.nom_value = nominal;
- audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_lvl_thr);
- audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_lvl_thr);
- // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
- if (tud_speed_get() == TUSB_SPEED_HIGH) {
- audio->feedback.compute.fifo_count.rate_const[0] /= 8;
- audio->feedback.compute.fifo_count.rate_const[1] /= 8;
- }
- } break;
-
- // nothing to do
- default:
- break;
- }
- }
+ // Prepare feedback computation parameters
+ TU_VERIFY(audiod_fb_params_prepare(func_id, alt));
#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// We are done - abort loop
@@ -1276,20 +1304,23 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
#endif
// Invoke callback
- return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_entity_cb(rhport, p_request, get_ctrl_buffer());
} else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
- return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_itf_cb(rhport, p_request, get_ctrl_buffer());
}
} break;
@@ -1299,8 +1330,32 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
- // Invoke callback
- return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ if (tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_in == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()) & 0x00FFFFFF;
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
+ }
+ }
+#endif
+
+ // Invoke callback
+ bool ret = tud_audio_set_req_ep_cb(rhport, p_request, get_ctrl_buffer());
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (ret && tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_out == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ audiod_fb_params_prepare(func_id, _audiod_fct[func_id].ep_out_alt);
+ }
+ }
+ }
+#endif
+ return ret;
} break;
// Unknown/Unsupported recipient
default:
@@ -1385,7 +1440,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
- TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ));
return true;
}
@@ -1399,6 +1454,8 @@ bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
return audiod_control_request(rhport, request);
} else if (stage == CONTROL_STAGE_DATA) {
return audiod_control_complete(rhport, request);
+ } else {
+ // nothing to do
}
return true;
@@ -1422,7 +1479,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// I assume here, that things above are handled by PHY
// All transmission is done - what remains to do is to inform job was completed
- tud_audio_int_xfer_cb(rhport);
+ tud_audio_int_done_cb(rhport);
return true;
}
@@ -1473,7 +1530,7 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
if (audio->ep_fb == ep_addr) {
// Schedule a transmit with the new value if EP is not busy
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
- audiod_fb_send(audio);
+ audiod_fb_send(func_id, true);
return true;
}
#endif
@@ -1485,30 +1542,72 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq) {
- // Check if frame interval is within sane limits
- // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
- // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
- // this lower limit ensures the measures feedback value has sufficient precision
- uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
- uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+ // Prepare feedback computation if endpoint is available
+ if (audio->ep_fb != 0) {
+ audio_feedback_params_t fb_param = {0};
- if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
- TU_LOG1(" UAC2 feedback interval too small\r\n");
- TU_BREAKPOINT();
- return false;
- }
+ tud_audio_feedback_params_cb(func_id, alt, &fb_param);
+ audio->feedback.compute_method = fb_param.method;
- // Check if parameters really allow for a power of two division
- if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) {
- audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
- audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq));
- } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
- audio->feedback.compute.float_const = (float) sample_freq / (float) mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
- } else {
- audio->feedback.compute.fixed.sample_freq = sample_freq;
- audio->feedback.compute.fixed.mclk_freq = mclk_freq;
+ // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
+ uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
+ audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
+ audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
+
+ switch (fb_param.method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: {
+ // Check if frame interval is within sane limits
+ // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+
+ // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
+ // this lower limit ensures the measures feedback value has sufficient precision
+ uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
+ uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+
+ if ((((1UL << k) * fb_param.sample_freq / fb_param.frequency.mclk_freq) + 1) > n_frame) {
+ TU_LOG1(" UAC2 feedback interval too small\r\n");
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ // Check if parameters really allow for a power of two division
+ if ((fb_param.frequency.mclk_freq % fb_param.sample_freq) == 0 && tu_is_power_of_two(fb_param.frequency.mclk_freq / fb_param.sample_freq)) {
+ audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
+ audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(fb_param.frequency.mclk_freq / fb_param.sample_freq));
+ } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
+ audio->feedback.compute.float_const = (float) fb_param.sample_freq / (float) fb_param.frequency.mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
+ } else {
+ audio->feedback.compute.fixed.sample_freq = fb_param.sample_freq;
+ audio->feedback.compute.fixed.mclk_freq = fb_param.frequency.mclk_freq;
+ }
+ } break;
+
+ case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
+ // Determine FIFO threshold
+ uint16_t fifo_threshold = fb_param.fifo_count.fifo_threshold ? fb_param.fifo_count.fifo_threshold : tu_fifo_depth(&audio->ep_out_ff) / 2;
+ audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_threshold;
+ audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_threshold) << 16;
+ // Avoid 64bit division
+ uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
+ audio->feedback.compute.fifo_count.nom_value = nominal;
+ audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_threshold);
+ audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_threshold);
+ // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ audio->feedback.compute.fifo_count.rate_const[0] /= 8;
+ audio->feedback.compute.fifo_count.rate_const[1] /= 8;
+ }
+ } break;
+
+ // nothing to do
+ default:
+ break;
+ }
}
return true;
@@ -1532,52 +1631,15 @@ static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_n
feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1];
}
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
- audio->feedback.value = feedback;
-}
-
-uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
- audiod_function_t *audio = &_audiod_fct[func_id];
- uint32_t feedback;
-
- switch (audio->feedback.compute_method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- feedback = (cycles << audio->feedback.compute.power_of_2);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
- uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
- feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
- } break;
-
- default:
- return 0;
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
}
-
- // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
- // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
- // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
- // (audio slot = channel count samples).
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
-
- tud_audio_n_fb_set(func_id, feedback);
-
- return feedback;
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
+ }
+ audio->feedback.value = feedback;
}
-bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
-
- _audiod_fct[func_id].feedback.value = feedback;
-
- return true;
-}
#endif
TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
@@ -1644,25 +1706,25 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
return false;
}
- // Crop length
- if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
-
// Copy into buffer
- TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz, data, (size_t) len));
+ TU_VERIFY(0 == tu_memcpy_s(get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ, data, (size_t) len));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- // Find data for sampling_frequency_control
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
- uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ // Find data for sampling_frequency_control
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
}
#endif
// Schedule transmit
- return tud_control_xfer(rhport, p_request, (void *) _audiod_fct[func_id].ctrl_buf, len);
+ return tud_control_xfer(rhport, p_request, get_ctrl_buffer(), len);
}
// Verify an entity with the given ID exists and returns also the corresponding driver index
@@ -1673,11 +1735,9 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) {
// Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);// Points to CS AC descriptor
- uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength + p_desc;
p_desc = tu_desc_next(p_desc);// Get past CS AC descriptor
- // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
- while (p_desc_end - p_desc > 0) {
+ while (_audiod_fct[i].p_desc_as - p_desc > 0) {
// Entity IDs are always at offset 3
if (p_desc[3] == entityID) {
*func_id = i;
@@ -1693,10 +1753,10 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) {
uint8_t i;
for (i = 0; i < CFG_TUD_AUDIO; i++) {
- if (_audiod_fct[i].p_desc) {
+ if (_audiod_fct[i].p_desc != NULL) {
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == itf) {
@@ -1718,8 +1778,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) {
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Advance past AC descriptors - EP we look for are streaming EPs
- uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
+ uint8_t const *p_desc = _audiod_fct[i].p_desc_as;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
@@ -1739,20 +1798,34 @@ static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t c
p_desc = tu_desc_next(p_desc);// Exclude standard AS interface descriptor of current alternate interface descriptor
- // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) {
- audio->n_channels_tx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
- audio->format_type_tx = (audio_format_type_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType);
- // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
- p_desc = tu_desc_next(p_desc);
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I) {
- audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
+ if (tud_audio_n_version(audiod_get_audio_fct_idx(audio)) == 1) {
+ p_desc = tu_desc_next(p_desc);// Exclude Class-Specific AS Interface Descriptor(4.5.2) to get to format type descriptor
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE) {
+ audio->format_type_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bFormatType;
+ if (audio->format_type_tx == AUDIO10_FORMAT_TYPE_I) {
+ audio->n_channels_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bNrChannels;
+ audio->n_bytes_per_sample_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bSubFrameSize;
+ // Save sample rate - needed when EP doesn't support setting sample rate
+ audio->sample_rate_tx = tu_unaligned_read32(((audio10_desc_type_I_format_n_t(1) const *) p_desc)->tSamFreq) & 0x00FFFFFF;
+ }
+ }
+ } else {
+ // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_AS_GENERAL) {
+ audio->n_channels_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
+ audio->format_type_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bFormatType;
+ // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+ p_desc = tu_desc_next(p_desc);
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE && ((audio20_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO20_FORMAT_TYPE_I) {
+ audio->n_bytes_per_sample_tx = ((audio20_desc_type_I_format_t const *) p_desc)->bSubslotSize;
+ }
}
}
}
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
- TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
+ // AUDIO20_FORMAT_TYPE_I = AUDIO10_FORMAT_TYPE_I
+ TU_VERIFY(audio->format_type_tx == AUDIO20_FORMAT_TYPE_I);
TU_VERIFY(audio->n_channels_tx);
TU_VERIFY(audio->n_bytes_per_sample_tx);
TU_VERIFY(audio->interval_tx);
@@ -1787,22 +1860,22 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
return true;
}
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_depth) {
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_depth) {
// Flow control need a FIFO size of at least 4*Navg
- if (norminal_size[1] && norminal_size[1] <= fifo_depth * 4) {
+ if (nominal_size[1] && nominal_size[1] <= fifo_depth * 4) {
// Use blackout to prioritize normal size packet
static int ctrl_blackout = 0;
uint16_t packet_size;
- uint16_t slot_size = norminal_size[2] - norminal_size[1];
- if (data_count < norminal_size[0]) {
+ uint16_t slot_size = nominal_size[2] - nominal_size[1];
+ if (data_count < nominal_size[0]) {
// If you get here frequently, then your I2S clock deviation is too big !
packet_size = 0;
- } else if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout) {
- packet_size = norminal_size[0];
+ } else if (data_count < (fifo_threshold - slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[0];
ctrl_blackout = 10;
- } else if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout) {
- packet_size = norminal_size[2];
- if (norminal_size[0] == norminal_size[1]) {
+ } else if (data_count > (fifo_threshold + slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[2];
+ if (nominal_size[0] == nominal_size[1]) {
// nav > INT(nav), eg. 44.1k, 88.2k
ctrl_blackout = 0;
} else {
@@ -1810,7 +1883,7 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
ctrl_blackout = 10;
}
} else {
- packet_size = norminal_size[1];
+ packet_size = nominal_size[1];
if (ctrl_blackout) {
ctrl_blackout--;
}
@@ -1825,11 +1898,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
#endif
// No security checks here - internal function only which should always succeed
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO; cnt++) {
- if (&_audiod_fct[cnt] == audio) return cnt;
- }
- return 0;
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
+ return (uint8_t) (audio - _audiod_fct);
}
#endif // (CFG_TUD_ENABLED && CFG_TUD_AUDIO)
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
index fd47c649d..1e0c46915 100644
--- a/src/class/audio/audio_device.h
+++ b/src/class/audio/audio_device.h
@@ -37,35 +37,10 @@
// All sizes are in bytes!
-#ifndef CFG_TUD_AUDIO_FUNC_1_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#endif
-
-// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
-#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
-
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
-#endif
-
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
+// 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_SZ
+#define CFG_TUD_AUDIO_CTRL_BUF_SZ 64
#endif
// End point sizes IN BYTES - Limits: Full Speed <= 1023, High Speed <= 1024
@@ -167,7 +142,8 @@
#endif
#endif
-// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
+// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a
+// virtual device where data is perfectly synchronized to USB clock.
#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
#endif
@@ -177,12 +153,6 @@
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
#endif
-// Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set().
-// Can be override by tud_audio_feedback_format_correction_cb()
-#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
-#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
-#endif
-
// Enable/disable interrupt EP (required for notifying host of control changes)
#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
@@ -205,6 +175,7 @@ extern "C" {
// CFG_TUD_AUDIO > 1
//--------------------------------------------------------------------+
bool tud_audio_n_mounted(uint8_t func_id);
+uint8_t tud_audio_n_version(uint8_t func_id);
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
uint16_t tud_audio_n_available (uint8_t func_id);
@@ -217,6 +188,8 @@ tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
bool tud_audio_n_clear_ep_in_ff (uint8_t func_id);
tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
+uint16_t tud_audio_n_get_ep_in_fifo_threshold(uint8_t func_id);
+void tud_audio_n_set_ep_in_fifo_threshold(uint8_t func_id, uint16_t threshold);
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
@@ -227,6 +200,7 @@ bool tud_audio_int_n_write (uint8_t func_id, const audio_
// Application API (Interface0)
//--------------------------------------------------------------------+
static inline bool tud_audio_mounted (void);
+static inline uint8_t tud_audio_version (void);
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
static inline uint16_t tud_audio_available (void);
@@ -248,12 +222,15 @@ static inline bool tud_audio_int_write (const audio_interru
#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.
+// 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);
+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
@@ -261,14 +238,18 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
#if CFG_TUD_AUDIO_ENABLE_EP_IN
// Invoked in ISR context once an audio packet was sent successfully.
-// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_write() in I2S receive callback.
-bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock),
+// call tud_audio_write() in I2S receive callback.
+bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in,
+ uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// Invoked in ISR context once an audio packet was received successfully.
-// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_read() in I2S transmit callback.
-bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock),
+// call tud_audio_read() in I2S transmit callback.
+bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out,
+ uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -277,42 +258,55 @@ bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t fu
//
// Option 1 - AUDIO_FEEDBACK_METHOD_FIFO_COUNT
// Feedback value is calculated within the audio driver by regulating the FIFO level to half fill.
-// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, well tested
-// (Windows, Linux, OSX) with a reliable result so far.
-// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is introduced.
+// Advantage: No SOF interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus
+// less CPU load, well tested (Windows, Linux, OSX) with a reliable result so far.
+// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is
+// introduced.
//
// Option 2 - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED / AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
-// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from
-// which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
+// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information
+// about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle
+// count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
// See tud_audio_set_fb_params() and tud_audio_feedback_update()
-// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a smaller delay is possible.
-// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point is the reading of the cycle counter value of f_m.
-// It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
-// Long-term drift could occur since error is accumulated.
+// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a
+// smaller delay is possible.
+// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point
+// is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -,
+// hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is
+// introduced). Long-term drift will cause the FIFO under/overflow, you still needs to correct it somehow.
//
// Option 3 - manual
-// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer.
-// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load.
-// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay is introduced.
+// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined
+// e.g. from some fill status of some FIFO buffer.
+// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus
+// less CPU load.
+// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay
+// is introduced.
-// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed.
+// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at
+// FB endpoint interval till it's changed.
//
-// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default,
-// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set or tud_audio_feedback_format_correction_cb()
-// return true, then tinyusb expects 16.16 format and handles the conversion to 10.14 on FS.
+// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus
+// Specification Revision 2.0 5.12.4.2). For simplicity, this function always uses 16.16 format. For FS devices,
+// the driver will automatically convert the value to 10.14 format.
//
-// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and it seems the
-// driver can work with either format.
+// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0
+// devices. On Linux and it seems the driver can work with either format.
//
-// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value.
+// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the
+// feature is used, the user can not set the feedback value.
//
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
-// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13
-// for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
-// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K)
-// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
+// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames
+// need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency
+// e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is
+// available and locked to f_s)
+// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where
+// P = min( ceil(log2(f_m / f_s)), K)
+// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles
+// within fb_n_frames
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
// Update feedback value with passed MCLK cycles since last time this update function is called.
@@ -339,10 +333,12 @@ typedef struct {
union {
struct {
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
- }frequency;
-
+ } frequency;
+ struct {
+ uint16_t fifo_threshold; // Target FIFO threshold level, default to half FIFO if not set
+ } fifo_count;
};
-}audio_feedback_params_t;
+} audio_feedback_params_t;
// Invoked when needed to set feedback parameters
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
@@ -352,13 +348,10 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba
// frame_number : current SOF count
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
-
-// (Full-Speed only) Callback to set feedback format correction is applied or not,
-// default to CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if not implemented.
-bool tud_audio_feedback_format_correction_cb(uint8_t func_id);
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+// Invoked when an interrupt notification transfer is complete
void tud_audio_int_done_cb(uint8_t rhport);
#endif
@@ -397,6 +390,10 @@ TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_mounted(void) {
return tud_audio_n_mounted(0);
}
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_audio_version(void) {
+ return tud_audio_n_version(0);
+}
+
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_available(void) {
@@ -431,6 +428,16 @@ TU_ATTR_ALWAYS_INLINE static inline tu_fifo_t* tud_audio_get_ep_in_ff(void) {
return tud_audio_n_get_ep_in_ff(0);
}
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_get_ep_in_fifo_threshold(void)
+{
+ return tud_audio_n_get_ep_in_fifo_threshold(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_audio_set_ep_in_fifo_threshold(uint16_t threshold)
+{
+ tud_audio_n_set_ep_in_fifo_threshold(0, threshold);
+}
+
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
@@ -461,7 +468,7 @@ void audiod_sof_isr (uint8_t rhport, uint32_t frame_count);
}
#endif
-#endif /* _TUSB_AUDIO_DEVICE_H_ */
+#endif /* TUSB_AUDIO_DEVICE_H_ */
/** @} */
/** @} */
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
index 3f1529cb6..b679d6ab6 100755
--- a/src/class/bth/bth_device.c
+++ b/src/class/bth/bth_device.c
@@ -67,7 +67,7 @@ static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) {
// skip if previous transfer not complete
TU_VERIFY(!usbd_edpt_busy(rhport, ep));
- TU_ASSERT(usbd_edpt_xfer(rhport, ep, data, len));
+ TU_ASSERT(usbd_edpt_xfer(rhport, ep, data, len, false));
return true;
}
@@ -169,7 +169,7 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
itf_desc = (tusb_desc_interface_t const *) tu_desc_next(tu_desc_next(desc_ep));
// Prepare for incoming data from host
- TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE, false), 0);
drv_len = hci_itf_size;
@@ -272,7 +272,7 @@ bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes);
// prepare for next data
- TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE, false));
} else if (ep_addr == _btd_itf.ep_ev) {
tud_bt_event_sent_cb((uint16_t) xferred_bytes);
} else if (ep_addr == _btd_itf.ep_acl_in) {
diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h
index 68f073bff..89a056dc8 100755
--- a/src/class/bth/bth_device.h
+++ b/src/class/bth/bth_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_BTH_DEVICE_H_
-#define _TUSB_BTH_DEVICE_H_
+#ifndef TUSB_BTH_DEVICE_H_
+#define TUSB_BTH_DEVICE_H_
#include <common/tusb_common.h>
#include <device/usbd.h>
@@ -114,4 +114,4 @@ bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t r
}
#endif
-#endif /* _TUSB_BTH_DEVICE_H_ */
+#endif /* TUSB_BTH_DEVICE_H_ */
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index 10ba16a7c..679723ba6 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -29,8 +29,8 @@
* Currently only Abstract Control Model subclass is supported
* @{ */
-#ifndef _TUSB_CDC_H__
-#define _TUSB_CDC_H__
+#ifndef TUSB_CDC_H__
+#define TUSB_CDC_H__
#include "common/tusb_common.h"
@@ -192,10 +192,10 @@ typedef enum {
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
} cdc_line_coding_stopbits_t;
-#define CDC_LINE_CODING_STOP_BITS_TEXT(STOP_BITS) ( \
- STOP_BITS == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \
- STOP_BITS == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \
- STOP_BITS == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?" )
+#define CDC_LINE_CODING_STOP_BITS_TEXT(STOP_BITS) ( \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?" )
// TODO Backward compatible for typos. Maybe removed in the future release
#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
@@ -211,11 +211,11 @@ typedef enum {
} cdc_line_coding_parity_t;
#define CDC_LINE_CODING_PARITY_CHAR(PARITY) ( \
- PARITY == CDC_LINE_CODING_PARITY_NONE ? 'N' : \
- PARITY == CDC_LINE_CODING_PARITY_ODD ? 'O' : \
- PARITY == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \
- PARITY == CDC_LINE_CODING_PARITY_MARK ? 'M' : \
- PARITY == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?' )
+ (PARITY) == CDC_LINE_CODING_PARITY_NONE ? 'N' : \
+ (PARITY) == CDC_LINE_CODING_PARITY_ODD ? 'O' : \
+ (PARITY) == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \
+ (PARITY) == CDC_LINE_CODING_PARITY_MARK ? 'M' : \
+ (PARITY) == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?' )
//--------------------------------------------------------------------+
// Management Element Notification (Notification Endpoint)
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index f1c4a3bbf..c499756b9 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -43,181 +43,143 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-
typedef struct {
uint8_t rhport;
uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
-
uint8_t ep_notify;
uint8_t line_state; // Bit 0: DTR, Bit 1: RTS
/*------------- From this point, data is not cleared by bus reset -------------*/
- char wanted_char;
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
+ char wanted_char;
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
+ tu_edpt_stream_t tx_stream;
+ tu_edpt_stream_t rx_stream;
- uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
-
- OSAL_MUTEX_DEF(rx_ff_mutex);
- OSAL_MUTEX_DEF(tx_ff_mutex);
+ uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
} cdcd_interface_t;
-#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding)
+// Skip local EP buffer if dedicated hw FIFO is supported
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
typedef struct {
- TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
- TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_CDC_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_CDC_TX_EPSIZE);
#if CFG_TUD_CDC_NOTIFY
TUD_EPBUF_TYPE_DEF(cdc_notify_msg_t, epnotify);
#endif
} cdcd_epbuf_t;
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
-
-static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
-
-static bool _prep_out_transaction(uint8_t itf) {
- const uint8_t rhport = 0;
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
-
- // Skip if usb is not ready yet
- TU_VERIFY(tud_ready() && p_cdc->ep_out);
-
- 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.
- // 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 >= CFG_TUD_CDC_EP_BUFSIZE);
-
- // claim endpoint
- TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_out));
-
- // fifo can be changed before endpoint is claimed
- available = tu_fifo_remaining(&p_cdc->rx_ff);
-
- if (available >= CFG_TUD_CDC_EP_BUFSIZE) {
- return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
- } else {
- // Release endpoint since we don't make any transfer
- usbd_edpt_release(p_cdc->rhport, p_cdc->ep_out);
- return false;
- }
-}
+#endif
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf) {
- (void) itf;
+ (void)itf;
}
TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) {
- (void) itf;
- (void) wanted_char;
+ (void)itf;
+ (void)wanted_char;
}
TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf) {
- (void) itf;
+ (void)itf;
}
TU_ATTR_WEAK void tud_cdc_notify_complete_cb(uint8_t itf) {
- (void) itf;
+ (void)itf;
}
TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
- (void) itf;
- (void) dtr;
- (void) rts;
+ (void)itf;
+ (void)dtr;
+ (void)rts;
}
-TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
- (void) itf;
- (void) p_line_coding;
+TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, const cdc_line_coding_t *p_line_coding) {
+ (void)itf;
+ (void)p_line_coding;
}
TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
- (void) itf;
- (void) duration_ms;
+ (void)itf;
+ (void)duration_ms;
}
//--------------------------------------------------------------------+
-// APPLICATION API
+// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
- TU_VERIFY(driver_cfg);
- _cdcd_cfg = *driver_cfg;
- return true;
+static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) {
+ const cdcd_interface_t *p_cdc = &_cdcd_itf[idx];
+ if (ep_addr == p_cdc->rx_stream.ep_addr || ep_addr == p_cdc->tx_stream.ep_addr ||
+ (ep_addr == p_cdc->ep_notify && ep_addr != 0)) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
}
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
bool tud_cdc_n_ready(uint8_t itf) {
- return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0;
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ TU_VERIFY(tud_ready());
+ const cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+
+ const bool in_opened = tu_edpt_stream_is_opened(&p_cdc->tx_stream);
+ const bool out_opened = tu_edpt_stream_is_opened(&p_cdc->rx_stream);
+ return in_opened && out_opened;
}
bool tud_cdc_n_connected(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ TU_VERIFY(tud_ready());
// DTR (bit 0) active is considered as connected
- return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
+ return tu_bit_test(_cdcd_itf[itf].line_state, 0);
}
uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
return _cdcd_itf[itf].line_state;
}
-void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t *coding) {
+ TU_VERIFY(itf < CFG_TUD_CDC, );
(*coding) = _cdcd_itf[itf].line_coding;
}
#if CFG_TUD_CDC_NOTIFY
-bool tud_cdc_n_notify_uart_state (uint8_t itf, const cdc_notify_uart_state_t *state) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
+bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ const cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));
- cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
- notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
- notify_msg->request.bRequest = CDC_NOTIF_SERIAL_STATE;
- notify_msg->request.wValue = 0;
- notify_msg->request.wIndex = p_cdc->itf_num;
- notify_msg->request.wLength = sizeof(cdc_notify_uart_state_t);
- notify_msg->serial_state = *state;
-
- return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_uart_state_t));
-}
-
-bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
- TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
- TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ cdc_notify_msg_t *msg_epbuf = msg;
+ #else
+ cdc_notify_msg_t *msg_epbuf = &_cdcd_epbuf[itf].epnotify;
+ *msg_epbuf = *msg;
+ #endif
- cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
- notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
- notify_msg->request.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE;
- notify_msg->request.wValue = 0;
- notify_msg->request.wIndex = p_cdc->itf_num;
- notify_msg->request.wLength = sizeof(cdc_notify_conn_speed_change_t);
- notify_msg->conn_speed_change = *conn_speed_change;
+ msg_epbuf->request.wIndex = p_cdc->itf_num;
- return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_conn_speed_change_t));
+ return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)msg_epbuf, 8 + msg_epbuf->request.wLength, false);
}
#endif
void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
+ TU_VERIFY(itf < CFG_TUD_CDC, );
_cdcd_itf[itf].wanted_char = wanted;
}
@@ -225,77 +187,54 @@ void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
// READ API
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_available(uint8_t itf) {
- return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ return tu_edpt_stream_read_available(&_cdcd_itf[itf].rx_stream);
}
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
- _prep_out_transaction(itf);
- return num_read;
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_read(&p_cdc->rx_stream, buffer, bufsize);
}
-bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
- return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
+bool tud_cdc_n_peek(uint8_t itf, uint8_t *chr) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ return tu_edpt_stream_peek(&_cdcd_itf[itf].rx_stream, chr);
}
void tud_cdc_n_read_flush(uint8_t itf) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- tu_fifo_clear(&p_cdc->rx_ff);
- _prep_out_transaction(itf);
+ TU_VERIFY(itf < CFG_TUD_CDC, );
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ tu_edpt_stream_clear(&p_cdc->rx_stream);
+ tu_edpt_stream_read_xfer(&p_cdc->rx_stream);
}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint16_t wr_count = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
-
- // flush if queue more than packet size
- if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
- #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
- || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
- #endif
- ) {
- tud_cdc_n_write_flush(itf);
- }
-
- return wr_count;
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write(&p_cdc->tx_stream, buffer, bufsize);
}
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
- TU_VERIFY(tud_ready(), 0); // Skip if usb is not ready yet
-
- // No data to send
- if (!tu_fifo_count(&p_cdc->tx_ff)) {
- return 0;
- }
-
- TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_in), 0); // Claim the endpoint
-
- // Pull data from FIFO
- const uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
-
- if (count) {
- TU_ASSERT(usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_in, p_epbuf->epin, count), 0);
- return count;
- } else {
- // Release endpoint since we don't make any transfer
- // Note: data is dropped if terminal is not connected
- usbd_edpt_release(p_cdc->rhport, p_cdc->ep_in);
- return 0;
- }
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write_xfer(&p_cdc->tx_stream);
}
uint32_t tud_cdc_n_write_available(uint8_t itf) {
- return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write_available(&p_cdc->tx_stream);
}
bool tud_cdc_n_write_clear(uint8_t itf) {
- return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ tu_edpt_stream_clear(&p_cdc->tx_stream);
+ return true;
}
//--------------------------------------------------------------------+
@@ -304,8 +243,7 @@ bool tud_cdc_n_write_clear(uint8_t itf) {
void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[i];
-
+ cdcd_interface_t *p_cdc = &_cdcd_itf[i];
p_cdc->wanted_char = (char) -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
@@ -314,44 +252,30 @@ void cdcd_init(void) {
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
- // Config RX fifo
- tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ uint8_t *epout_buf = _cdcd_epbuf[i].epout;
+ uint8_t *epin_buf = _cdcd_epbuf[i].epin;
+ #endif
+
+ tu_edpt_stream_init(&p_cdc->rx_stream, false, false, false, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, epout_buf);
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
- tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, _cdcd_cfg.tx_overwritabe_if_not_connected);
-
- #if OSAL_MUTEX_REQUIRED
- osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
- osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
- TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
-
- tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
- tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
- #endif
+ tu_edpt_stream_init(&p_cdc->tx_stream, false, true, CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED, p_cdc->tx_ff_buf,
+ CFG_TUD_CDC_TX_BUFSIZE, epin_buf);
}
}
bool cdcd_deinit(void) {
- #if OSAL_MUTEX_REQUIRED
- for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
- osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
- osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
-
- if (mutex_rd) {
- osal_mutex_delete(mutex_rd);
- tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
- }
-
- if (mutex_wr) {
- osal_mutex_delete(mutex_wr);
- tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
- }
+ tu_edpt_stream_deinit(&p_cdc->rx_stream);
+ tu_edpt_stream_deinit(&p_cdc->tx_stream);
}
- #endif
-
return true;
}
@@ -360,74 +284,92 @@ void cdcd_reset(uint8_t rhport) {
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
-
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
- if (!_cdcd_cfg.rx_persistent) {
- tu_fifo_clear(&p_cdc->rx_ff);
- }
- if (!_cdcd_cfg.tx_persistent) {
- tu_fifo_clear(&p_cdc->tx_ff);
- }
- tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
+
+ tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED); // back to default
+ tu_edpt_stream_close(&p_cdc->rx_stream);
+ tu_edpt_stream_close(&p_cdc->tx_stream);
}
}
uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
// Only support ACM subclass
- TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
+ TU_VERIFY(TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass,
+ 0);
- // Find available interface
- cdcd_interface_t* p_cdc;
- uint8_t cdc_id;
- for (cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
- p_cdc = &_cdcd_itf[cdc_id];
- if (p_cdc->ep_in == 0) {
- break;
- }
- }
+ const uint8_t cdc_id = find_cdc_itf(0); // Find available interface
TU_ASSERT(cdc_id < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[cdc_id];
//------------- Control Interface -------------//
p_cdc->rhport = rhport;
p_cdc->itf_num = itf_desc->bInterfaceNumber;
- uint16_t drv_len = sizeof(tusb_desc_interface_t);
- const uint8_t* p_desc = tu_desc_next(itf_desc);
+ const uint8_t *p_desc = (const uint8_t *)itf_desc;
+ const uint8_t *desc_end = p_desc + max_len;
- // Communication Functional Descriptors
- while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
- drv_len += tu_desc_len(p_desc);
+ // Skip all class-specific descriptor
+ p_desc = tu_desc_next(itf_desc);
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
p_desc = tu_desc_next(p_desc);
}
+ // notification endpoint (optional)
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
- // notification endpoint
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
p_cdc->ep_notify = desc_ep->bEndpointAddress;
- drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
- //------------- Data Interface (if any) -------------//
- if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((const tusb_desc_interface_t*) p_desc)->bInterfaceClass)) {
- // next to endpoint descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ //------------- Data Interface (optional) -------------//
+ if (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
+ const tusb_desc_interface_t *data_itf_desc = (const tusb_desc_interface_t *)p_desc;
+ if (TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass) {
+ for (uint8_t e = 0; e < data_itf_desc->bNumEndpoints; e++) {
+ if (!tu_desc_in_bounds(p_desc, desc_end)) {
+ break;
+ }
+ p_desc = tu_desc_next(p_desc);
- // Open endpoint pair
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
- drv_len += 2 * sizeof(tusb_desc_endpoint_t);
- }
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream;
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_CDC_TX_EPSIZE);
+
+ #if CFG_TUD_CDC_TX_PERSISTENT
+ tu_edpt_stream_write_xfer(stream_tx); // flush pending data
+ #else
+ tu_edpt_stream_clear(stream_tx);
+ #endif
+ } else {
+ tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream;
+ #if CFG_TUD_CDC_RX_NEED_ZLP
+ const uint16_t xfer_len = CFG_TUD_CDC_RX_EPSIZE;
+ #else
+ const uint16_t xfer_len = tu_edpt_packet_size(desc_ep);
+ #endif
- // Prepare for incoming data
- _prep_out_transaction(cdc_id);
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep, xfer_len);
- return drv_len;
+ #if !CFG_TUD_CDC_RX_PERSISTENT
+ tu_edpt_stream_clear(stream_rx);
+ #endif
+
+ TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare for incoming data
+ }
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+
+ return (uint16_t)(p_desc - (const uint8_t *)itf_desc);
}
// Invoked when a control transfer occurred on an interface of this class
@@ -456,6 +398,8 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
} else if (stage == CONTROL_STAGE_ACK) {
tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ } else {
+ // nothing to do
}
break;
@@ -481,16 +425,17 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
p_cdc->line_state = (uint8_t) request->wValue;
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
- if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
- tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
- } else {
- tu_fifo_set_overwritable(&p_cdc->tx_ff, false);
- }
+ #if CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED
+ const bool is_overwritable = !dtr;
+ #else
+ const bool is_overwritable = false;
+ #endif
+ tu_fifo_set_overwritable(&p_cdc->tx_stream.ff, is_overwritable);
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
-
- // Invoke callback
- tud_cdc_line_state_cb(itf, dtr, rts);
+ tud_cdc_line_state_cb(itf, dtr, rts); // invoke callback
+ } else {
+ // nothing to do
}
break;
@@ -500,6 +445,8 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
} else if (stage == CONTROL_STAGE_ACK) {
TU_LOG_DRV(" Send Break\r\n");
tud_cdc_send_break_cb(itf, request->wValue);
+ } else {
+ // nothing to do
}
break;
@@ -511,58 +458,68 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
}
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
- (void) result;
+ (void)rhport;
+ (void)result;
- uint8_t itf;
- cdcd_interface_t* p_cdc;
-
- // Identify which interface to use
- for (itf = 0; itf < CFG_TUD_CDC; itf++) {
- p_cdc = &_cdcd_itf[itf];
- if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in) || (ep_addr == p_cdc->ep_notify)) {
- break;
- }
- }
+ uint8_t itf = find_cdc_itf(ep_addr);
TU_ASSERT(itf < CFG_TUD_CDC);
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ tu_edpt_stream_t *stream_rx = &p_cdc->rx_stream;
+ tu_edpt_stream_t *stream_tx = &p_cdc->tx_stream;
- // Received new data
- if (ep_addr == p_cdc->ep_out) {
- tu_fifo_write_n(&p_cdc->rx_ff, p_epbuf->epout, (uint16_t) xferred_bytes);
+ // Received new data, move to fifo
+ if (ep_addr == stream_rx->ep_addr) {
+ tu_edpt_stream_read_xfer_complete(stream_rx, xferred_bytes);
- // Check for wanted char and invoke callback if needed
- if (((signed char) p_cdc->wanted_char) != -1) {
- for (uint32_t i = 0; i < xferred_bytes; i++) {
- if ((p_cdc->wanted_char == p_epbuf->epout[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) {
- tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ // Check for wanted char and invoke wanted callback
+ if (((signed char)p_cdc->wanted_char) != -1) {
+ tu_fifo_buffer_info_t buf_info;
+ tu_fifo_get_read_info(&stream_rx->ff, &buf_info);
+
+ // find backward
+ uint8_t *ptr;
+ if (buf_info.wrapped.len > 0) {
+ ptr = buf_info.wrapped.ptr + buf_info.wrapped.len - 1; // last byte of wrap buffer
+ } else if (buf_info.linear.len > 0) {
+ ptr = buf_info.linear.ptr + buf_info.linear.len - 1; // last byte of linear buffer
+ } else {
+ ptr = NULL; // no data
+ }
+
+ if (ptr != NULL) {
+ for (uint32_t i = 0; i < xferred_bytes; i++) {
+ if (p_cdc->wanted_char == (char)*ptr) {
+ tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ break; // only invoke once per transfer, even if multiple wanted chars are present
+ }
+
+ if (ptr == buf_info.wrapped.ptr) {
+ ptr = buf_info.linear.ptr + buf_info.linear.len - 1; // last byte of linear buffer
+ } else if (ptr == buf_info.linear.ptr) {
+ break; // reached the beginning
+ } else {
+ ptr--;
+ }
}
}
}
- // invoke receive callback (if there is still data)
- if (!tu_fifo_empty(&p_cdc->rx_ff)) {
+ // invoke receive callback if there is still data
+ if (!tu_edpt_stream_empty(stream_rx)) {
tud_cdc_rx_cb(itf);
}
- // prepare for OUT transaction
- _prep_out_transaction(itf);
+ tu_edpt_stream_read_xfer(stream_rx); // prepare for more data
}
// Data sent to host, we continue to fetch from tx fifo to send.
- // Note: This will cause incorrect baudrate set in line coding.
- // Though maybe the baudrate is not really important !!!
- if (ep_addr == p_cdc->ep_in) {
- // invoke transmit callback to possibly refill tx fifo
- tud_cdc_tx_complete_cb(itf);
+ // Note: This will cause incorrect baudrate set in line coding. Though maybe the baudrate is not really important!
+ if (ep_addr == stream_tx->ep_addr) {
+ tud_cdc_tx_complete_cb(itf); // invoke callback to possibly refill tx fifo
- if (0 == tud_cdc_n_write_flush(itf)) {
- // If there is no data left, a ZLP should be sent if
- // xferred_bytes is multiple of EP Packet size and not zero
- if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
- if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0));
- }
- }
+ if (0 == tu_edpt_stream_write_xfer(stream_tx)) {
+ // If there is no data left, a ZLP should be sent if needed
+ tu_edpt_stream_write_zlp_if_needed(stream_tx, xferred_bytes);
}
}
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 9673b9807..9ac6bc58a 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -29,6 +29,10 @@
#include "cdc.h"
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
@@ -36,52 +40,74 @@
#define CFG_TUD_CDC_NOTIFY 0
#endif
-#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE)
- #warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name
- #define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE
+#ifndef CFG_TUD_CDC_TX_BUFSIZE
+ #define CFG_TUD_CDC_TX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
-#ifndef CFG_TUD_CDC_EP_BUFSIZE
- #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#ifndef CFG_TUD_CDC_RX_BUFSIZE
+ #define CFG_TUD_CDC_RX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
-#ifdef __cplusplus
- extern "C" {
+// EP_BUFSIZE is separated to RX_EPSIZE and TX_EPSIZE
+#ifndef CFG_TUD_CDC_RX_EPSIZE
+ #ifdef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_RX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE
+ #else
+ #define CFG_TUD_CDC_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
-//--------------------------------------------------------------------+
-// Driver Configuration
-//--------------------------------------------------------------------+
-typedef struct TU_ATTR_PACKED {
- uint8_t rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
- uint8_t tx_persistent : 1; // keep tx fifo data even with reset or disconnect
- uint8_t tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
-} tud_cdc_configure_t;
+#ifndef CFG_TUD_CDC_TX_EPSIZE
+ #ifdef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_EP_BUFSIZE
+ #else
+ #define CFG_TUD_CDC_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
+#endif
-#define TUD_CDC_CONFIGURE_DEFAULT() { \
- .rx_persistent = 0, \
- .tx_persistent = 0, \
- .tx_overwritabe_if_not_connected = 1, \
-}
+// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP.
+#ifndef CFG_TUD_CDC_RX_NEED_ZLP
+ #define CFG_TUD_CDC_RX_NEED_ZLP 0
+#endif
-// Configure CDC driver behavior
-bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
+// Keep rx fifo data even with bus reset or disconnect
+#ifndef CFG_TUD_CDC_RX_PERSISTENT
+ #define CFG_TUD_CDC_RX_PERSISTENT 0
+#endif
-// Backward compatible
-#define tud_cdc_configure_fifo_t tud_cdc_configure_t
-#define tud_cdc_configure_fifo tud_cdc_configure
+// Keep tx fifo data even with bus reset or disconnect
+#ifndef CFG_TUD_CDC_TX_PERSISTENT
+ #define CFG_TUD_CDC_TX_PERSISTENT 0
+#endif
+
+// If not connected, tx fifo can be overwritten
+#ifndef CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED
+ #define CFG_TUD_CDC_TX_OVERWRITABLE_IF_NOT_CONNECTED 1
+#endif
+
+// Backward compatible: tud_cdc_configure_t and tud_cdc_configure() are no longer used.
+// Configuration is now done via compile-time macros above.
+typedef struct {
+ bool rx_persistent;
+ bool tx_persistent;
+ bool tx_overwritabe_if_not_connected;
+} tud_cdc_configure_t;
+
+#define tud_cdc_configure(_cfg) ((void)(_cfg))
+#define tud_cdc_configure_fifo_t tud_cdc_configure_t
+#define tud_cdc_configure_fifo(_cfg) ((void)(_cfg))
//--------------------------------------------------------------------+
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
//--------------------------------------------------------------------+
-// Check if interface is ready
+// Check if the interface is ready
bool tud_cdc_n_ready(uint8_t itf);
-// Check if terminal is connected to this port
+// Check if the terminal is connected to this port
bool tud_cdc_n_connected(uint8_t itf);
-// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+// Get the current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
uint8_t tud_cdc_n_get_line_state(uint8_t itf);
// Get current line encoding: bit rate, stop bits parity etc ..
@@ -127,16 +153,41 @@ uint32_t tud_cdc_n_write_flush(uint8_t itf);
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
uint32_t tud_cdc_n_write_available(uint8_t itf);
-// Clear the transmit FIFO
+// Clear the TX FIFO
bool tud_cdc_n_write_clear(uint8_t itf);
-
#if CFG_TUD_CDC_NOTIFY
+bool tud_cdc_n_notify_msg(uint8_t itf, cdc_notify_msg_t *msg);
+
// Send UART status notification: DCD, DSR etc ..
-bool tud_cdc_n_notify_uart_state(uint8_t itf, const cdc_notify_uart_state_t *state);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_n_notify_uart_state(uint8_t itf,
+ const cdc_notify_uart_state_t *state) {
+ cdc_notify_msg_t notify_msg;
+ notify_msg.request.bmRequestType = CDC_REQ_TYPE_NOTIF;
+ notify_msg.request.bRequest = CDC_NOTIF_SERIAL_STATE;
+ notify_msg.request.wValue = 0;
+ notify_msg.request.wIndex = 0; // filled later
+ notify_msg.request.wLength = sizeof(cdc_notify_uart_state_t);
+ notify_msg.serial_state = *state;
+ return tud_cdc_n_notify_msg(itf, &notify_msg);
+}
// Send connection speed change notification
-bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change);
+TU_ATTR_ALWAYS_INLINE static inline bool
+tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t *conn_speed_change) {
+ cdc_notify_msg_t notify_msg;
+ notify_msg.request.bmRequestType = CDC_REQ_TYPE_NOTIF;
+ notify_msg.request.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE;
+ notify_msg.request.wValue = 0;
+ notify_msg.request.wIndex = 0; // filled later
+ notify_msg.request.wLength = sizeof(cdc_notify_conn_speed_change_t);
+ notify_msg.conn_speed_change = *conn_speed_change;
+ return tud_cdc_n_notify_msg(itf, &notify_msg);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_msg(cdc_notify_msg_t *msg) {
+ return tud_cdc_n_notify_msg(0, msg);
+}
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_uart_state(const cdc_notify_uart_state_t* state) {
return tud_cdc_n_notify_uart_state(0, state);
@@ -257,4 +308,4 @@ bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t re
}
#endif
-#endif /* _TUSB_CDC_DEVICE_H_ */
+#endif /* TUSB_CDC_DEVICE_H_ */
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index beef03eff..4441222c8 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -29,6 +29,8 @@
#include "tusb_option.h"
+#include <stdint.h>
+
#if (CFG_TUH_ENABLED && CFG_TUH_CDC)
#include "host/usbh.h"
@@ -41,13 +43,14 @@
#include "serial/pl2303.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
-#ifndef CFG_TUH_CDC_LOG_LEVEL
- #define CFG_TUH_CDC_LOG_LEVEL 2
-#endif
+ #ifndef CFG_TUH_CDC_LOG_LEVEL
+ #define CFG_TUH_CDC_LOG_LEVEL 2
+ #endif
-#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
-#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \
- serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__)
+ #define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
+ #define TU_LOG_CDC(_cdc, _format, ...) \
+ TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \
+ serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__)
//--------------------------------------------------------------------+
// Host CDC Interface
@@ -96,6 +99,7 @@ typedef struct {
typedef struct {
TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE);
TUH_EPBUF_DEF(rx, CFG_TUH_CDC_RX_EPSIZE);
+ TUH_EPBUF_DEF(ctrl, 8);
} cdch_epbuf_t;
static cdch_interface_t cdch_data[CFG_TUH_CDC];
@@ -113,63 +117,59 @@ static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer);
static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer);
//------------- ACM prototypes -------------//
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-
-static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-//------------- FTDI prototypes -------------//
-#if CFG_TUH_CDC_FTDI
+ //------------- FTDI prototypes -------------//
+ #if CFG_TUH_CDC_FTDI
static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
-static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
-static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t *xfer);
-static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-#endif
+static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void ftdi_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ #endif
-//------------- CP210X prototypes -------------//
-#if CFG_TUH_CDC_CP210X
+ //------------- CP210X prototypes -------------//
+ #if CFG_TUH_CDC_CP210X
static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
-static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-
-static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-#endif
+static uint16_t cp210x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ #endif
-//------------- CH34x prototypes -------------//
-#if CFG_TUH_CDC_CH34X
+ //------------- CH34x prototypes -------------//
+ #if CFG_TUH_CDC_CH34X
static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
-static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-
-static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-#endif
+static uint16_t ch34x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool ch34x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ #endif
-//------------- PL2303 prototypes -------------//
-#if CFG_TUH_CDC_PL2303
+ //------------- PL2303 prototypes -------------//
+ #if CFG_TUH_CDC_PL2303
static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST};
static const pl2303_type_data_t pl2303_type_data[PL2303_TYPE_COUNT] = {PL2303_TYPE_DATA};
-static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-
-static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-#endif
+static uint16_t pl2303_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool pl2303_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool pl2303_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ #endif
//------------- Common -------------//
enum {
@@ -197,11 +197,12 @@ enum {
typedef bool (*serial_driver_func_t)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
typedef struct {
- uint16_t const (*vid_pid_list)[2];
- uint16_t const vid_pid_count;
- bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
- bool (*const process_set_config)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer);
- void (*const request_complete)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer); // internal request complete handler to update line state
+ const uint16_t (*vid_pid_list)[2];
+ const uint16_t vid_pid_count;
+ uint16_t (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+ bool (*const process_set_config)(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+ // internal request complete handler to update line state
+ void (*const request_complete)(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
serial_driver_func_t set_control_line_state, set_baudrate, set_data_format, set_line_coding;
@@ -216,88 +217,91 @@ typedef struct {
#define DRIVER_NAME_DECLARE(_str)
#endif
+// clang-format off
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
{
- .vid_pid_list = NULL,
- .vid_pid_count = 0,
- .open = acm_open,
- .process_set_config = acm_process_set_config,
- .request_complete = acm_internal_control_complete,
- .set_control_line_state = acm_set_control_line_state,
- .set_baudrate = acm_set_line_coding,
- .set_data_format = acm_set_line_coding,
- .set_line_coding = acm_set_line_coding,
- DRIVER_NAME_DECLARE("ACM")
+ .vid_pid_list = NULL,
+ .vid_pid_count = 0,
+ .open = acm_open,
+ .process_set_config = acm_process_set_config,
+ .request_complete = acm_internal_control_complete,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_line_coding,
+ .set_data_format = acm_set_line_coding,
+ .set_line_coding = acm_set_line_coding,
+ DRIVER_NAME_DECLARE("ACM")
},
#if CFG_TUH_CDC_FTDI
{
- .vid_pid_list = ftdi_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
- .open = ftdi_open,
- .process_set_config = ftdi_proccess_set_config,
- .request_complete = ftdi_internal_control_complete,
- .set_control_line_state = ftdi_set_modem_ctrl,
- .set_baudrate = ftdi_set_baudrate,
- .set_data_format = ftdi_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("FTDI")
+ .vid_pid_list = ftdi_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+ .open = ftdi_open,
+ .process_set_config = ftdi_process_set_config,
+ .request_complete = ftdi_internal_control_complete,
+ .set_control_line_state = ftdi_set_modem_ctrl,
+ .set_baudrate = ftdi_set_baudrate,
+ .set_data_format = ftdi_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("FTDI")
},
#endif
#if CFG_TUH_CDC_CP210X
{
- .vid_pid_list = cp210x_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
- .open = cp210x_open,
- .process_set_config = cp210x_process_set_config,
- .request_complete = cp210x_internal_control_complete,
- .set_control_line_state = cp210x_set_modem_ctrl,
- .set_baudrate = cp210x_set_baudrate,
- .set_data_format = cp210x_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("CP210x")
+ .vid_pid_list = cp210x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+ .open = cp210x_open,
+ .process_set_config = cp210x_process_set_config,
+ .request_complete = cp210x_internal_control_complete,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_data_format = cp210x_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CP210x")
},
#endif
#if CFG_TUH_CDC_CH34X
{
- .vid_pid_list = ch34x_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
- .open = ch34x_open,
- .process_set_config = ch34x_process_set_config,
- .request_complete = ch34x_internal_control_complete,
+ .vid_pid_list = ch34x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+ .open = ch34x_open,
+ .process_set_config = ch34x_process_set_config,
+ .request_complete = ch34x_internal_control_complete,
- .set_control_line_state = ch34x_set_modem_ctrl,
- .set_baudrate = ch34x_set_baudrate,
- .set_data_format = ch34x_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("CH34x")
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_data_format = ch34x_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CH34x")
},
#endif
#if CFG_TUH_CDC_PL2303
{
- .vid_pid_list = pl2303_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
- .open = pl2303_open,
- .process_set_config = pl2303_process_set_config,
- .request_complete = pl2303_internal_control_complete,
- .set_control_line_state = pl2303_set_modem_ctrl,
- .set_baudrate = pl2303_set_line_coding,
- .set_data_format = pl2303_set_line_coding,
- .set_line_coding = pl2303_set_line_coding,
- DRIVER_NAME_DECLARE("PL2303")
+ .vid_pid_list = pl2303_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
+ .open = pl2303_open,
+ .process_set_config = pl2303_process_set_config,
+ .request_complete = pl2303_internal_control_complete,
+ .set_control_line_state = pl2303_set_modem_ctrl,
+ .set_baudrate = pl2303_set_line_coding,
+ .set_data_format = pl2303_set_line_coding,
+ .set_line_coding = pl2303_set_line_coding,
+ DRIVER_NAME_DECLARE("PL2303")
}
#endif
};
+// clang-format on
TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
+static bool open_ep_stream_pair(cdch_interface_t *p_cdc, const tusb_desc_endpoint_t *desc_ep);
TU_ATTR_ALWAYS_INLINE static inline cdch_interface_t * get_itf(uint8_t idx) {
TU_ASSERT(idx < CFG_TUH_CDC, NULL);
@@ -364,7 +368,7 @@ static cdch_interface_t* get_itf_by_xfer(const tuh_xfer_t * xfer) {
#endif
default:
- break;
+ break; // unknown driver
}
}
}
@@ -389,8 +393,6 @@ static cdch_interface_t * make_new_itf(uint8_t daddr, tusb_desc_interface_t cons
return NULL;
}
-static bool open_ep_stream_pair(cdch_interface_t * p_cdc , tusb_desc_endpoint_t const *desc_ep);
-
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
@@ -471,25 +473,26 @@ bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t * line_coding)
uint32_t tuh_cdc_write(uint8_t idx, void const * buffer, uint32_t bufsize) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- return tu_edpt_stream_write(p_cdc->daddr, &p_cdc->stream.tx, buffer, bufsize);
+ return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize);
}
uint32_t tuh_cdc_write_flush(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- return tu_edpt_stream_write_xfer(p_cdc->daddr, &p_cdc->stream.tx);
+ return tu_edpt_stream_write_xfer(&p_cdc->stream.tx);
}
bool tuh_cdc_write_clear(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- return tu_edpt_stream_clear(&p_cdc->stream.tx);
+ tu_edpt_stream_clear(&p_cdc->stream.tx);
+ return true;
}
uint32_t tuh_cdc_write_available(uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- return tu_edpt_stream_write_available(p_cdc->daddr, &p_cdc->stream.tx);
+ return tu_edpt_stream_write_available(&p_cdc->stream.tx);
}
//--------------------------------------------------------------------+
@@ -499,7 +502,7 @@ uint32_t tuh_cdc_write_available(uint8_t idx) {
uint32_t tuh_cdc_read (uint8_t idx, void * buffer, uint32_t bufsize) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- return tu_edpt_stream_read(p_cdc->daddr, &p_cdc->stream.rx, buffer, bufsize);
+ return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize);
}
uint32_t tuh_cdc_read_available(uint8_t idx) {
@@ -518,9 +521,9 @@ bool tuh_cdc_read_clear (uint8_t idx) {
cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
- bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx);
- tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
- return ret;
+ tu_edpt_stream_clear(&p_cdc->stream.rx);
+ (void)tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
+ return true;
}
//--------------------------------------------------------------------+
@@ -602,7 +605,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding,
p_cdc->requested_line.coding = *line_coding;
p_cdc->user_complete_cb = complete_cb;
- if (driver->set_line_coding) {
+ if (driver->set_line_coding != NULL) {
// driver support set_line_coding request
TU_VERIFY(driver->set_line_coding(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data));
@@ -611,7 +614,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding,
}
} else {
// driver does not support set_line_coding and need 2 stage to set baudrate and data format separately
- if (complete_cb) {
+ if (complete_cb != NULL) {
// non-blocking
TU_VERIFY(driver->set_baudrate(p_cdc, cdch_set_line_coding_stage1_baudrate_complete, user_data));
} else {
@@ -619,7 +622,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding,
xfer_result_t result = XFER_RESULT_INVALID;
TU_VERIFY(driver->set_baudrate(p_cdc, NULL, (uintptr_t) &result));
- if (user_data) {
+ if (user_data != 0) {
*((xfer_result_t *) user_data) = result;
}
TU_VERIFY(result == XFER_RESULT_SUCCESS);
@@ -627,7 +630,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding,
result = XFER_RESULT_INVALID;
TU_VERIFY(driver->set_data_format(p_cdc, NULL, (uintptr_t) &result));
- if (user_data) {
+ if (user_data != 0) {
*((xfer_result_t *) user_data) = result;
}
TU_VERIFY(result == XFER_RESULT_SUCCESS);
@@ -648,13 +651,10 @@ bool cdch_init(void) {
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
cdch_interface_t *p_cdc = &cdch_data[i];
cdch_epbuf_t *epbuf = &cdch_epbuf[i];
- tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false,
- p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- epbuf->tx, CFG_TUH_CDC_TX_EPSIZE);
-
- tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
- p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
- epbuf->rx, CFG_TUH_CDC_RX_EPSIZE);
+ TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf,
+ CFG_TUH_CDC_TX_BUFSIZE, epbuf->tx));
+ TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, p_cdc->stream.rx_ff_buf,
+ CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx));
}
return true;
@@ -663,8 +663,8 @@ bool cdch_init(void) {
bool cdch_deinit(void) {
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
cdch_interface_t *p_cdc = &cdch_data[i];
- tu_edpt_stream_deinit(&p_cdc->stream.tx);
- tu_edpt_stream_deinit(&p_cdc->stream.rx);
+ (void)tu_edpt_stream_deinit(&p_cdc->stream.tx);
+ (void)tu_edpt_stream_deinit(&p_cdc->stream.rx);
}
return true;
}
@@ -674,11 +674,9 @@ void cdch_close(uint8_t daddr) {
cdch_interface_t *p_cdc = &cdch_data[idx];
if (p_cdc->daddr == daddr) {
TU_LOG_CDC(p_cdc, "close");
+ tuh_cdc_umount_cb(idx); // invoke callback
- // Invoke application callback
- tuh_cdc_umount_cb(idx);
-
- p_cdc->daddr = 0;
+ p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
p_cdc->mounted = false;
tu_edpt_stream_close(&p_cdc->stream.tx);
@@ -696,13 +694,12 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_ASSERT(p_cdc);
if (ep_addr == p_cdc->stream.tx.ep_addr) {
- // invoke tx complete callback to possibly refill tx fifo
- tuh_cdc_tx_complete_cb(idx);
+ tuh_cdc_tx_complete_cb(idx); // invoke transmit complete callback
- if (0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx)) {
+ if (0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx)) {
// If there is no data left, a ZLP should be sent if:
// - xferred_bytes is multiple of EP Packet size and not zero
- tu_edpt_stream_write_zlp_if_needed(daddr, &p_cdc->stream.tx, xferred_bytes);
+ (void)tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes);
}
} else if (ep_addr == p_cdc->stream.rx.ep_addr) {
#if CFG_TUH_CDC_FTDI
@@ -718,16 +715,15 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
#endif
{
tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
-
tuh_cdc_rx_cb(idx); // invoke receive callback
}
// prepare for next transfer if needed
- tu_edpt_stream_read_xfer(daddr, &p_cdc->stream.rx);
+ tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else if (ep_addr == p_cdc->ep_notif) {
// TODO handle notification endpoint
} else {
- TU_ASSERT(false);
+ return false;
}
return true;
@@ -736,27 +732,23 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
-
static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t const *desc_ep) {
for (size_t i = 0; i < 2; i++) {
- TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
- TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
+ const uint8_t ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ tu_edpt_stream_t *stream = (ep_dir == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
+ tu_edpt_stream_open(stream, p_cdc->daddr, desc_ep, tu_edpt_packet_size(desc_ep));
+ tu_edpt_stream_clear(stream);
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- tu_edpt_stream_open(&p_cdc->stream.rx, desc_ep);
- } else {
- tu_edpt_stream_open(&p_cdc->stream.tx, desc_ep);
- }
-
- desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep);
+ desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep);
}
return true;
}
-bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
- (void) rhport;
+uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ (void)rhport;
// For CDC: only support ACM subclass
// Note: Protocol 0xFF can be RNDIS device
if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
@@ -765,52 +757,36 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
} else if (SERIAL_DRIVER_COUNT > 1 &&
TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
uint16_t vid, pid;
- TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
+ TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid), 0);
- for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
- const cdch_serial_driver_t *driver = &serial_drivers[dr];
+ for (size_t drv = 1; drv < SERIAL_DRIVER_COUNT; drv++) {
+ const cdch_serial_driver_t *driver = &serial_drivers[drv];
for (size_t i = 0; i < driver->vid_pid_count; i++) {
if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
- const bool ret = driver->open(daddr, itf_desc, max_len);
- TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, ret ? "OK" : "FAILED");
- return ret;
+ const uint16_t drv_len = driver->open(daddr, itf_desc, max_len);
+ TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name,
+ drv_len > 0 ? "OK" : "FAILED");
+ return drv_len;
}
}
}
+ } else {
+ // not supported class
}
- return false;
-}
-
-bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
- tusb_control_request_t request;
- request.wIndex = tu_htole16((uint16_t) itf_num);
- uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
- cdch_interface_t *p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- TU_LOG_CDC(p_cdc, "set config");
-
- // fake transfer to kick-off process_set_config()
- tuh_xfer_t xfer;
- xfer.daddr = daddr;
- xfer.result = XFER_RESULT_SUCCESS;
- xfer.setup = &request;
- xfer.user_data = 0; // initial state 0
- cdch_process_set_config(&xfer);
-
- return true;
+ return 0;
}
static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
if (success) {
const uint8_t idx = get_idx_by_ptr(p_cdc);
- p_cdc->mounted = true;
+ p_cdc->mounted = true;
tuh_cdc_mount_cb(idx);
// Prepare for incoming data
- tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
+ tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else {
// clear the interface entry
- p_cdc->daddr = 0;
+ p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
}
@@ -819,6 +795,33 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset);
}
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
+ const uint8_t idx = tuh_cdc_itf_get_index(daddr, itf_num);
+ cdch_interface_t *p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ TU_LOG_CDC(p_cdc, "set config");
+
+ // fake transfer to kick-off process_set_config()
+ tusb_control_request_t request;
+ request.wIndex = tu_htole16((uint16_t)itf_num);
+
+ tuh_xfer_t xfer;
+ xfer.daddr = daddr;
+ xfer.ep_addr = 0;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.setup = &request;
+ xfer.complete_cb = NULL;
+ xfer.buffer = NULL;
+ xfer.user_data = 0; // initial state 0
+
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
+ if (!driver->process_set_config(p_cdc, &xfer)) {
+ set_config_complete(p_cdc, false);
+ }
+
+ return true;
+}
+
static void cdch_process_set_config(tuh_xfer_t *xfer) {
cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
@@ -830,13 +833,16 @@ static void cdch_process_set_config(tuh_xfer_t *xfer) {
}
}
+// return false if there is no active transfer
static bool set_line_state_on_enum(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
enum {
ENUM_SET_LINE_CODING = 0,
ENUM_SET_LINE_CONTROL,
ENUM_SET_LINE_COMPLETE,
};
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
const uint8_t idx = get_idx_by_ptr(p_cdc);
+ #endif
const uintptr_t state = xfer->user_data;
switch (state) {
@@ -894,7 +900,7 @@ static void cdch_internal_control_complete(tuh_xfer_t *xfer) {
// Invoke application callback
xfer->complete_cb = p_cdc->user_complete_cb;
- if (xfer->complete_cb) {
+ if (xfer->complete_cb != NULL) {
xfer->complete_cb(xfer);
}
}
@@ -910,7 +916,7 @@ static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer) {
TU_ASSERT(driver->set_data_format(p_cdc, cdch_set_line_coding_stage2_data_format_complete, xfer->user_data),);
} else {
xfer->complete_cb = p_cdc->user_complete_cb;
- if (xfer->complete_cb) {
+ if (xfer->complete_cb != NULL) {
xfer->complete_cb(xfer);
}
}
@@ -926,7 +932,7 @@ static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer) {
}
xfer->complete_cb = p_cdc->user_complete_cb;
- if (xfer->complete_cb) {
+ if (xfer->complete_cb != NULL) {
xfer->complete_cb(xfer);
}
}
@@ -950,12 +956,12 @@ static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *x
break;
default:
- break;
+ break; // unknown request
}
}
static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_VERIFY(p_cdc->acm.capability.support_line_request);
+ TU_VERIFY(p_cdc->acm.capability.support_line_request != 0);
const tusb_control_request_t request = {
.bmRequestType_bit = {
@@ -982,7 +988,7 @@ static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_t co
}
static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_VERIFY(p_cdc->acm.capability.support_line_request);
+ TU_VERIFY(p_cdc->acm.capability.support_line_request != 0);
TU_VERIFY((p_cdc->requested_line.coding.data_bits >= 5 && p_cdc->requested_line.coding.data_bits <= 8) ||
p_cdc->requested_line.coding.data_bits == 16);
@@ -998,15 +1004,16 @@ static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_
.wLength = tu_htole16((uint16_t) sizeof(cdc_line_coding_t))
};
- // use usbh enum buf to hold line coding since user line_coding variable does not live long enough
- uint8_t *enum_buf = usbh_get_enum_buf();
- memcpy(enum_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t));
+ // use local ctrl buf to hold line coding since user line_coding variable does not live long enough
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ uint8_t *ctrl_buf = cdch_epbuf[idx].ctrl;
+ memcpy(ctrl_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t));
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1019,19 +1026,19 @@ enum {
CONFIG_ACM_COMPLETE = 0
};
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
- uint8_t const *p_desc_end = ((uint8_t const *) itf_desc) + max_len;
+static uint16_t acm_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ const uint8_t *p_desc = (const uint8_t *)itf_desc;
+ const uint8_t *desc_end = p_desc + max_len;
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
-
+ TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_ACM;
//------------- Control Interface -------------//
- uint8_t const *p_desc = tu_desc_next(itf_desc);
+ p_desc = tu_desc_next(p_desc);
// Communication Functional Descriptors
- while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+ while ((p_desc < desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
// save ACM bmCapabilities
p_cdc->acm.capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
@@ -1042,26 +1049,27 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint1
// Open notification endpoint of control interface if any
if (itf_desc->bNumEndpoints == 1) {
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
- tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
-
- TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
- if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass)) {
- // next to endpoint descriptor
- p_desc = tu_desc_next(p_desc);
+ if (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
+ const tusb_desc_interface_t *data_itf = (const tusb_desc_interface_t *)p_desc;
+ if (data_itf->bInterfaceClass == TUSB_CLASS_CDC_DATA) {
+ p_desc = tu_desc_next(p_desc); // next to endpoint descriptor
- // data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (const tusb_desc_endpoint_t *)p_desc), 0);
+ p_desc += data_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t);
+ }
}
- return true;
+ return (uint16_t)((uintptr_t)p_desc - (uintptr_t)itf_desc);
}
static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@@ -1136,7 +1144,6 @@ static inline bool ftdi_sio_reset(cdch_interface_t *p_cdc, tuh_xfer_cb_t complet
// internal control complete to update state such as line state, line_coding
static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t *xfer) {
- TU_VERIFY(xfer->result == XFER_RESULT_SUCCESS,);
const tusb_control_request_t * setup = xfer->setup;
if (xfer->result == XFER_RESULT_SUCCESS) {
if (setup->bRequest == FTDI_SIO_SET_MODEM_CTRL_REQUEST &&
@@ -1168,10 +1175,10 @@ static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete
static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint32_t index_value = ftdi_get_divisor(p_cdc);
- TU_VERIFY(index_value);
+ TU_VERIFY(index_value != 0);
uint16_t value = (uint16_t) index_value;
uint16_t index = (uint16_t) (index_value >> 16);
- if (p_cdc->ftdi.channel) {
+ if (p_cdc->ftdi.channel != 0) {
index = (uint16_t) ((index << 8) | p_cdc->ftdi.channel);
}
@@ -1195,46 +1202,40 @@ enum {
CONFIG_FTDI_COMPLETE
};
-static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// FTDI Interface includes 1 vendor interface + 2 bulk endpoints
TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff &&
- itf_desc->bNumEndpoints == 2);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
+ itf_desc->bNumEndpoints == 2,
+ 0);
+ const uint16_t drv_len =
+ (uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
+ TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
// endpoint pair
- tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
- /*
- * NOTE: Some customers have programmed FT232R/FT245R devices
- * with an endpoint size of 0 - not good.
- */
- TU_ASSERT(desc_ep->wMaxPacketSize != 0);
+ /* NOTE: Some users have programmed FT232R/FT245R devices
+ * with an endpoint size of 0 !!! */
+ TU_ASSERT(desc_ep->wMaxPacketSize != 0, 0);
- // data endpoints expected to be in pairs
- return open_ep_stream_pair(p_cdc, desc_ep);
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
+
+ return drv_len;
}
-static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
const uintptr_t state = xfer->user_data;
switch (state) {
// from here sequence overtaken from Linux Kernel function ftdi_port_probe()
case CONFIG_FTDI_DETERMINE_TYPE:
// determine type
- if (p_cdc->bInterfaceNumber == 0) {
- TU_ASSERT(ftdi_determine_type(p_cdc));
- } else {
- // other interfaces have same type as interface 0
- uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0);
- cdch_interface_t const *p_cdc_itf0 = get_itf(idx_itf0);
- TU_ASSERT(p_cdc_itf0);
- p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type;
- }
+ TU_ASSERT(ftdi_determine_type(p_cdc));
TU_ATTR_FALLTHROUGH;
case CONFIG_FTDI_WRITE_LATENCY:
@@ -1365,7 +1366,7 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
uint8_t divfrac[8] = {0, 3, 2, 4, 1, 5, 6, 7};
uint32_t divisor;
/* divisor shifted 3 bits to the left */
- uint32_t divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
+ uint32_t divisor3 = tu_div_round_nearest(base, 2 * baud);
divisor = divisor3 >> 3;
divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
@@ -1373,6 +1374,8 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
divisor = 0;
} else if (divisor == 0x4001) /* 1.5 */ {
divisor = 1;
+ } else {
+ // nothing to do
}
return divisor;
}
@@ -1387,7 +1390,7 @@ static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base) {
uint32_t divisor3;
/* hi-speed baud rate is 10-bit sampling instead of 16-bit */
- divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud);
+ divisor3 = tu_div_round_nearest(8 * base, 10 * baud);
divisor = divisor3 >> 3;
divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
@@ -1396,12 +1399,13 @@ static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base) {
divisor = 0;
} else if (divisor == 0x4001) /* 1.5 */ {
divisor = 1;
+ } else {
+ // nothing to do
}
- /*
- * Set this bit to turn off a divide by 2.5 on baud rate generator
+
+ /* Set this bit to turn off a divide by 2.5 on baud rate generator
* This enables baud rates up to 12Mbaud but cannot reach below 1200
- * baud with this bit set
- */
+ * baud with this bit set */
divisor |= 0x00020000;
return divisor;
}
@@ -1413,7 +1417,7 @@ static inline uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud) {
static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) {
uint32_t baud = p_cdc->requested_line.coding.bit_rate;
uint32_t div_value = 0;
- TU_VERIFY(baud);
+ TU_VERIFY(baud != 0);
switch (p_cdc->ftdi.chip_type) {
case FTDI_UNKNOWN:
@@ -1489,7 +1493,7 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) {
//------------- Control Request -------------//
static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16_t value,
- uint8_t * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t const * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
@@ -1502,19 +1506,20 @@ static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t * enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t * ctrl_buf = NULL;
if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1553,14 +1558,15 @@ static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t
p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate;
break;
- default: break;
+ default:
+ break; // unsupported request
}
}
static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
// Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support"
uint32_t baud_le = tu_htole32(p_cdc->requested_line.coding.bit_rate);
- return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data);
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t const *) &baud_le, 4, complete_cb, user_data);
}
static bool cp210x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
@@ -1583,21 +1589,23 @@ enum {
CONFIG_CP210X_COMPLETE
};
-static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+static uint16_t cp210x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// CP210x Interface includes 1 vendor interface + 2 bulk endpoints
- TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2, 0);
+ const uint16_t drv_len =
+ (uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
+ TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
- // endpoint pair
- tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
-
// data endpoints expected to be in pairs
- return open_ep_stream_pair(p_cdc, desc_ep);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+
+ return drv_len;
}
static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@@ -1635,7 +1643,7 @@ static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t *p_cdc);
//------------- Control Request -------------//
static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_t request,
- uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ uint16_t value, uint16_t index, uint8_t const *buffer, uint16_t length,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request_setup = {
.bmRequestType_bit = {
@@ -1649,13 +1657,14 @@ static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t *enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t *ctrl_buf = NULL;
- if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- if (direction == TUSB_DIR_OUT) {
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ if (length > 0) {
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ if (buffer && direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
}
@@ -1663,7 +1672,7 @@ static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request_setup,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1677,8 +1686,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_out(cdch_interface_t *p_c
}
TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_in(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index,
- uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+ uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, NULL, buffersize,
complete_cb, user_data);
}
@@ -1687,12 +1696,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool ch34x_write_reg(cdch_interface_t *p_cdc
return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
}
-//static bool ch34x_read_reg_request ( cdch_interface_t * p_cdc, uint16_t reg,
-// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-//{
-// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
-//}
-
//------------- Driver API -------------//
// internal control complete to update state such as line state, encoding
@@ -1714,7 +1717,8 @@ static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t
p_cdc->line.coding.data_bits = p_cdc->requested_line.coding.data_bits;
break;
- default: break;
+ default:
+ break; // unsupported
}
break;
@@ -1722,19 +1726,20 @@ static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t
p_cdc->line.control_state = p_cdc->requested_line.control_state;
break;
- default: break;
+ default:
+ break; // unsupported request
}
}
static bool ch34x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
const uint8_t lcr = ch34x_get_lcr(p_cdc);
- TU_VERIFY(lcr);
+ TU_VERIFY(lcr > 0);
return ch34x_write_reg(p_cdc, CH32X_REG16_LCR2_LCR, lcr, complete_cb, user_data);
}
static bool ch34x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
const uint16_t div_ps = ch34x_get_divisor_prescaler(p_cdc);
- TU_VERIFY(div_ps);
+ TU_VERIFY(div_ps > 0);
return ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, complete_cb, user_data);
}
@@ -1755,29 +1760,30 @@ enum {
CONFIG_CH34X_COMPLETE
};
-static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
+static uint16_t ch34x_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
- TU_VERIFY(itf_desc->bNumEndpoints == 3);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+ TU_VERIFY(itf_desc->bNumEndpoints == 3, 0);
+ const uint16_t drv_len =
+ (uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
+ TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
// data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
- desc_ep += 2;
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
+ desc_ep = (const tusb_desc_endpoint_t *)((uintptr_t)desc_ep + 2 * sizeof(tusb_desc_endpoint_t));
// Interrupt endpoint: not used for now
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
- TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
- TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
- return true;
+ return drv_len;
}
static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
@@ -1786,8 +1792,7 @@ static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
switch (state) {
case CONFIG_CH34X_READ_VERSION: {
- uint8_t* enum_buf = usbh_get_enum_buf();
- TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, enum_buf, 2,
+ TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, 2,
cdch_process_set_config, CONFIG_CH34X_SERIAL_INIT));
break;
}
@@ -1917,7 +1922,8 @@ static uint8_t ch34x_get_lcr(cdch_interface_t *p_cdc) {
lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
break;
- default: break;
+ default:
+ break; // invalid parity
}
// 1.5 stop bits not supported
@@ -1941,7 +1947,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_
//------------- Control Request -------------//
static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t requesttype,
- uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ uint16_t value, uint16_t index, uint8_t const *buffer, uint16_t length,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request_setup = {
.bmRequestType = requesttype,
@@ -1951,13 +1957,14 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t *enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t *ctrl_buf = NULL;
- if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- if (request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) {
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ if (length > 0) {
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ if (buffer && request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
}
@@ -1965,7 +1972,7 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request_setup,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1973,10 +1980,10 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
return tuh_control_xfer(&xfer);
}
-static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value, uint8_t *buf,
+static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t request = p_cdc->pl2303.type == PL2303_TYPE_HXN ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
- return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, complete_cb, user_data);
+ return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, NULL, 1, complete_cb, user_data);
}
static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_t index,
@@ -1986,9 +1993,8 @@ static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_
}
static inline bool pl2303_supports_hx_status(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint8_t buf = 0;
return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0,
- &buf, 1, complete_cb, user_data);
+ NULL, 1, complete_cb, user_data);
}
//static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) {
@@ -2000,13 +2006,15 @@ static inline bool pl2303_supports_hx_status(cdch_interface_t *p_cdc, tuh_xfer_c
// return pl2303_set_request(p_cdc, PL2303_BREAK_REQUEST, PL2303_BREAK_REQUEST_TYPE, state, 0, NULL, 0);
//}
-static inline int pl2303_clear_halt(cdch_interface_t *p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static inline bool
+pl2303_clear_halt(cdch_interface_t *p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
/* we don't care if it wasn't halted first. in fact some devices
* (like some ibmcam model 1 units) seem to expect hosts to make
* this request for iso endpoints, which can't halt!
*/
- return pl2303_set_request(p_cdc, TUSB_REQ_CLEAR_FEATURE, PL2303_CLEAR_HALT_REQUEST_TYPE, TUSB_REQ_FEATURE_EDPT_HALT, endp,
- NULL, 0, complete_cb, user_data);
+ return pl2303_set_request(
+ p_cdc, TUSB_REQ_CLEAR_FEATURE, PL2303_CLEAR_HALT_REQUEST_TYPE, TUSB_REQ_FEATURE_EDPT_HALT, endp, NULL, 0,
+ complete_cb, user_data);
}
//------------- Driver API -------------//
@@ -2088,13 +2096,15 @@ enum {
CONFIG_PL2303_COMPLETE
};
-static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+static uint16_t pl2303_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
// PL2303 Interface includes 1 vendor interface + 1 interrupt endpoints + 2 bulk
- TU_VERIFY(itf_desc->bNumEndpoints == 3);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+ TU_VERIFY(itf_desc->bNumEndpoints == 3, 0);
+ const uint16_t drv_len =
+ (uint16_t)(sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_VERIFY(drv_len <= max_len, 0);
cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
+ TU_VERIFY(p_cdc, 0);
p_cdc->serial_drid = SERIAL_DRIVER_PL2303;
p_cdc->pl2303.quirks = 0;
@@ -2103,23 +2113,21 @@ static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
// Interrupt endpoint: not used for now
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
- TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
- TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
desc_ep += 1;
// data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep), 0);
- return true;
+ return drv_len;
}
static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
// state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status()
const uintptr_t state = xfer->user_data;
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1);
- uint8_t* enum_buf = usbh_get_enum_buf();
pl2303_type_t type;
switch (state) {
@@ -2131,10 +2139,9 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
if (type == PL2303_TYPE_NEED_SUPPORTS_HX_STATUS) {
TU_ASSERT(pl2303_supports_hx_status(p_cdc, cdch_process_set_config, CONFIG_PL2303_READ1));
break;
- } else {
- // no transfer triggered and continue with CONFIG_PL2303_READ1
- TU_ATTR_FALLTHROUGH;
}
+ // no transfer triggered and continue with CONFIG_PL2303_READ1
+ TU_ATTR_FALLTHROUGH;
case CONFIG_PL2303_READ1:
// get supports_hx_status, type and quirks (step 2), do special read
@@ -2151,7 +2158,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE1));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_WRITE1));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2167,7 +2174,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ2:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ3));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_READ3));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2175,7 +2182,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ3:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ4));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, cdch_process_set_config, CONFIG_PL2303_READ4));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2183,7 +2190,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ4:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE2));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_WRITE2));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2199,7 +2206,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ5:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ6));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_READ6));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2207,7 +2214,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ6:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE3));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, cdch_process_set_config, CONFIG_PL2303_WRITE3));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2379,10 +2386,12 @@ static pl2303_type_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) {
return PL2303_TYPE_HXN;
default:
- break;
+ break; // unknown device
}
break;
- default: break;
+
+ default:
+ break; // unknown device
}
TU_LOG_CDC(p_cdc, "unknown device type bcdUSB = 0x%04x", desc_dev.bcdUSB);
@@ -2444,8 +2453,9 @@ static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_B
*/
baseline = 12000000 * 32;
mantissa = baseline / baud;
- if (mantissa == 0)
+ if (mantissa == 0) {
mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */
+ }
exponent = 0;
while (mantissa >= 512) {
if (exponent < 7) {
@@ -2517,7 +2527,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_
* Use direct method for supported baud rates, otherwise use divisors.
* Newer chip types do not support divisor encoding.
*/
- if (type_data->no_divisors) {
+ if (type_data->no_divisors != 0) {
baud_sup = baud;
} else {
baud_sup = pl2303_get_supported_baud_rate(baud);
@@ -2525,7 +2535,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_
if (baud == baud_sup) {
baud = pl2303_encode_baud_rate_direct(buf, baud);
- } else if (type_data->alt_divisors) {
+ } else if (type_data->alt_divisors != 0) {
baud = pl2303_encode_baud_rate_divisor_alt(buf, baud);
} else {
baud = pl2303_encode_baud_rate_divisor(buf, baud);
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index bf6711d7e..1b1709b18 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -24,13 +24,13 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_CDC_HOST_H_
-#define _TUSB_CDC_HOST_H_
+#ifndef TUSB_CDC_HOST_H_
+#define TUSB_CDC_HOST_H_
#include "cdc.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
//--------------------------------------------------------------------+
@@ -39,22 +39,22 @@
// RX FIFO size
#ifndef CFG_TUH_CDC_RX_BUFSIZE
-#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// RX Endpoint size
#ifndef CFG_TUH_CDC_RX_EPSIZE
-#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MAX
#endif
// TX FIFO size
#ifndef CFG_TUH_CDC_TX_BUFSIZE
-#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// TX Endpoint size
#ifndef CFG_TUH_CDC_TX_EPSIZE
-#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MAX
#endif
//--------------------------------------------------------------------+
@@ -67,7 +67,7 @@ uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get Interface information
// return true if index is correct and interface is currently mounted
-bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t *info);
// Check if an interface is mounted
bool tuh_cdc_mounted(uint8_t idx);
@@ -75,7 +75,7 @@ bool tuh_cdc_mounted(uint8_t idx);
// Get local (cached) line state
// This function should return correct values if tuh_cdc_set_control_line_state() / tuh_cdc_get_control_line_state()
// are invoked previously or CFG_TUH_CDC_LINE_STATE_ON_ENUM is defined.
-bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t* line_state);
+bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t *line_state);
// Get current DTR status
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_get_dtr(uint8_t idx) {
@@ -100,7 +100,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
// NOTE: This function does not make any USB transfer request to device.
-bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
+bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t *line_coding);
#define tuh_cdc_get_local_line_coding tuh_cdc_get_line_coding_local // backward compatibility
@@ -112,7 +112,7 @@ bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
uint32_t tuh_cdc_write_available(uint8_t idx);
// Write to cdc interface
-uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
+uint32_t tuh_cdc_write(uint8_t idx, const void *buffer, uint32_t bufsize);
// Force sending data if possible, return number of forced bytes
uint32_t tuh_cdc_write_flush(uint8_t idx);
@@ -128,13 +128,13 @@ bool tuh_cdc_write_clear(uint8_t idx);
uint32_t tuh_cdc_read_available(uint8_t idx);
// Read from cdc interface
-uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
+uint32_t tuh_cdc_read(uint8_t idx, void *buffer, uint32_t bufsize);
// Get a byte from RX FIFO without removing it
-bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
+bool tuh_cdc_peek(uint8_t idx, uint8_t *ch);
// Clear the received FIFO
-bool tuh_cdc_read_clear (uint8_t idx);
+bool tuh_cdc_read_clear(uint8_t idx);
//--------------------------------------------------------------------+
// Control Request API
@@ -149,16 +149,18 @@ bool tuh_cdc_read_clear (uint8_t idx);
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Set DTR
-TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- cdc_line_control_state_t line_state = { .dtr = dtr_state };
- line_state.rts = tuh_cdc_get_rts(idx);
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data) {
+ cdc_line_control_state_t line_state = {.dtr = dtr_state};
+ line_state.rts = tuh_cdc_get_rts(idx);
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
}
// Request to Set RTS
-TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- cdc_line_control_state_t line_state = { .rts = rts_state };
- line_state.dtr = tuh_cdc_get_dtr(idx);
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data) {
+ cdc_line_control_state_t line_state = {.rts = rts_state};
+ line_state.dtr = tuh_cdc_get_dtr(idx);
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
}
@@ -166,11 +168,13 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_s
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to set data format
-bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Request to Set Line Coding = baudrate + data format
// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
-bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+bool tuh_cdc_set_line_coding(uint8_t idx, const cdc_line_coding_t *line_coding, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data);
// Request to Get Line Coding (ACM only)
// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
@@ -179,11 +183,13 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// Connect by set both DTR, RTS
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
+ return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb,
+ user_data);
}
// Disconnect by clear both DTR, RTS
-TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data) {
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
}
@@ -192,7 +198,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfe
// Each Function will make a USB control transfer request to/from device the function will block until request is
// complete. The function will return the transfer request result
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx, uint16_t line_state) {
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx,
+ uint16_t line_state) {
TU_API_SYNC(tuh_cdc_set_control_line_state, idx, line_state);
}
@@ -208,11 +215,13 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_baudrate_sync
TU_API_SYNC(tuh_cdc_set_baudrate, idx, baudrate);
}
-TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits,
+ uint8_t parity, uint8_t data_bits) {
TU_API_SYNC(tuh_cdc_set_data_format, idx, stop_bits, parity, data_bits);
}
-TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_line_coding_sync(uint8_t idx, cdc_line_coding_t const* line_coding) {
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t
+tuh_cdc_set_line_coding_sync(uint8_t idx, const cdc_line_coding_t *line_coding) {
TU_API_SYNC(tuh_cdc_set_line_coding, idx, line_coding);
}
@@ -244,15 +253,15 @@ extern void tuh_cdc_tx_complete_cb(uint8_t idx);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-bool cdch_init (void);
-bool cdch_deinit (void);
-bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
-bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void cdch_close (uint8_t dev_addr);
+bool cdch_init(void);
+bool cdch_deinit(void);
+uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void cdch_close(uint8_t dev_addr);
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_CDC_HOST_H_ */
+#endif /* TUSB_CDC_HOST_H_ */
diff --git a/src/class/cdc/cdc_rndis.h b/src/class/cdc/cdc_rndis.h
index ad153e0ac..fbbd43206 100644
--- a/src/class/cdc/cdc_rndis.h
+++ b/src/class/cdc/cdc_rndis.h
@@ -30,8 +30,8 @@
* \defgroup CDC_RNDIS_Common Common Definitions
* @{ */
-#ifndef _TUSB_CDC_RNDIS_H_
-#define _TUSB_CDC_RNDIS_H_
+#ifndef TUSB_CDC_RNDIS_H_
+#define TUSB_CDC_RNDIS_H_
#include "cdc.h"
@@ -295,7 +295,7 @@ typedef enum
}
#endif
-#endif /* _TUSB_CDC_RNDIS_H_ */
+#endif /* TUSB_CDC_RNDIS_H_ */
/** @} */
/** @} */
diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c
deleted file mode 100644
index e975ea440..000000000
--- a/src/class/cdc/cdc_rndis_host.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * 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.
- */
-
-#include "tusb_option.h"
-
-#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
-#include "cdc_host.h"
-#include "cdc_rndis_host.h"
-
-#if 0 // TODO remove subtask related macros later
-// Sub Task
-#define OSAL_SUBTASK_BEGIN
-#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
-
-#define STASK_RETURN(_error) return _error;
-#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
-#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
-#endif
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
-
-CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
-CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
-
-static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
-
-// TODO Microsoft requires message length for any get command must be at least 4096 bytes
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static tusb_error_t rndis_body_subtask(void);
-static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
- uint8_t * p_mess, uint32_t mess_length,
- uint8_t *p_response );
-
-//--------------------------------------------------------------------+
-// APPLICATION API
-//--------------------------------------------------------------------+
-tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
-{
- TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
- TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
-
- memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
-
- return TUSB_ERROR_NONE;
-}
-
-//--------------------------------------------------------------------+
-// IMPLEMENTATION
-//--------------------------------------------------------------------+
-
-// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
-// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
-// forever loop cannot have any return at all.
-OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
-{
- OSAL_TASK_BEGIN
- rndis_body_subtask();
- OSAL_TASK_END
-}
-
-static tusb_error_t rndis_body_subtask(void)
-{
- static uint8_t relative_addr;
-
- OSAL_SUBTASK_BEGIN
-
- for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
- {
-
- }
-
- tusb_time_delay_ms_api(100);
-
- OSAL_SUBTASK_END
-}
-
-//--------------------------------------------------------------------+
-// RNDIS-CDC Driver API
-//--------------------------------------------------------------------+
-void rndish_init(void)
-{
- tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
-
- //------------- Task creation -------------//
-
- //------------- semaphore creation for notification pipe -------------//
- for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
- {
- rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
- }
-}
-
-void rndish_close(uint8_t dev_addr)
-{
- osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
-// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
-}
-
-
-static rndis_msg_initialize_t const msg_init =
-{
- .type = RNDIS_MSG_INITIALIZE,
- .length = sizeof(rndis_msg_initialize_t),
- .request_id = 1, // TODO should use some magic number
- .major_version = 1,
- .minor_version = 0,
- .max_xfer_size = 0x4000 // TODO mimic windows
-};
-
-static rndis_msg_query_t const msg_query_permanent_addr =
-{
- .type = RNDIS_MSG_QUERY,
- .length = sizeof(rndis_msg_query_t)+6,
- .request_id = 1,
- .oid = RNDIS_OID_802_3_PERMANENT_ADDRESS,
- .buffer_length = 6,
- .buffer_offset = 20,
-};
-
-static rndis_msg_set_t const msg_set_packet_filter =
-{
- .type = RNDIS_MSG_SET,
- .length = sizeof(rndis_msg_set_t)+4,
- .request_id = 1,
- .oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
- .buffer_length = 4,
- .buffer_offset = 20,
-};
-
-tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
-{
- tusb_error_t error;
-
- OSAL_SUBTASK_BEGIN
-
- //------------- Message Initialize -------------//
- memcpy(msg_payload, &msg_init, sizeof(rndis_msg_initialize_t));
- STASK_INVOKE(
- send_message_get_response_subtask( dev_addr, p_cdc,
- msg_payload, sizeof(rndis_msg_initialize_t),
- msg_payload),
- error
- );
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
-
- // TODO currently not support multiple data packets per xfer
- rndis_msg_initialize_cmplt_t * const p_init_cmpt = (rndis_msg_initialize_cmplt_t *) msg_payload;
- STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
- p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
- rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
-
- //------------- Message Query 802.3 Permanent Address -------------//
- memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t));
- tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
-
- STASK_INVOKE(
- send_message_get_response_subtask( dev_addr, p_cdc,
- msg_payload, sizeof(rndis_msg_query_t) + 6,
- msg_payload),
- error
- );
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
-
- rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload;
- STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
- memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
-
- //------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
- memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t));
- tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
- ((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST);
-
- STASK_INVOKE(
- send_message_get_response_subtask( dev_addr, p_cdc,
- msg_payload, sizeof(rndis_msg_set_t) + 4,
- msg_payload),
- error
- );
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
-
- rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload;
- STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
-
- tusbh_cdc_rndis_mounted_cb(dev_addr);
-
- OSAL_SUBTASK_END
-}
-
-void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
-{
- if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
- {
- osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
- }
-}
-
-//--------------------------------------------------------------------+
-// INTERNAL & HELPER
-//--------------------------------------------------------------------+
-static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
- uint8_t * p_mess, uint32_t mess_length,
- uint8_t *p_response)
-{
- tusb_error_t error;
-
- OSAL_SUBTASK_BEGIN
-
- //------------- Send RNDIS Control Message -------------//
- STASK_INVOKE(
- usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
- CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
- mess_length, p_mess),
- error
- );
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
-
- //------------- waiting for Response Available notification -------------//
- (void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8);
- osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
- STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
-
- //------------- Get RNDIS Message Initialize Complete -------------//
- STASK_INVOKE(
- usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
- CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
- RNDIS_MSG_PAYLOAD_MAX, p_response),
- error
- );
- if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
-
- OSAL_SUBTASK_END
-}
-
-//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
-//{
-// tusb_error_t error;
-//
-// OSAL_SUBTASK_BEGIN
-//
-// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
-// {
-// .type = RNDIS_MSG_INITIALIZE,
-// .length = sizeof(rndis_msg_initialize_t),
-// .request_id = 1, // TODO should use some magic number
-// .major_version = 1,
-// .minor_version = 0,
-// .max_xfer_size = 0x4000 // TODO mimic windows
-// };
-//
-//
-//
-// OSAL_SUBTASK_END
-//}
-#endif
diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h
index 8abf74f11..9bd56cef4 100644
--- a/src/class/cdc/serial/ftdi_sio.h
+++ b/src/class/cdc/serial/ftdi_sio.h
@@ -215,17 +215,4 @@ typedef struct ftdi_private {
#define FTDI_NOT_POSSIBLE -1
#define FTDI_REQUESTED -2
-// division and round function overtaken from math.h
-#define DIV_ROUND_CLOSEST(x, divisor)( \
-{ \
- typeof(x) __x = x; \
- typeof(divisor) __d = divisor; \
- (((typeof(x))-1) > 0 || \
- ((typeof(divisor))-1) > 0 || \
- (((__x) > 0) == ((__d) > 0))) ? \
- (((__x) + ((__d) / 2)) / (__d)) : \
- (((__x) - ((__d) / 2)) / (__d)); \
-} \
-)
-
#endif //TUSB_FTDI_SIO_H
diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h
index 114c827b8..8cd63656a 100644
--- a/src/class/dfu/dfu.h
+++ b/src/class/dfu/dfu.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_H_
-#define _TUSB_DFU_H_
+#ifndef TUSB_DFU_H_
+#define TUSB_DFU_H_
#include "common/tusb_common.h"
@@ -116,4 +116,4 @@ TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
}
#endif
-#endif /* _TUSB_DFU_H_ */
+#endif /* TUSB_DFU_H_ */
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index 0d2b63b57..ee57621b8 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -50,21 +50,19 @@
typedef struct {
uint8_t attrs;
uint8_t alt;
+ uint8_t state;
+ uint8_t status;
- dfu_state_t state;
- dfu_status_t status;
-
- bool flashing_in_progress;
+ bool flashing_in_progress;
uint16_t block;
uint16_t length;
} dfu_state_ctx_t;
-// Only a single dfu state is allowed
static dfu_state_ctx_t _dfu_ctx;
-CFG_TUD_MEM_SECTION static struct {
- TUD_EPBUF_DEF(transfer_buf, CFG_TUD_DFU_XFER_BUFSIZE);
-} _dfu_epbuf;
+#if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE
+TU_ATTR_ALIGNED(4) uint8_t _transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
+#endif
static void reset_state(void) {
_dfu_ctx.state = DFU_IDLE;
@@ -72,6 +70,15 @@ static void reset_state(void) {
_dfu_ctx.flashing_in_progress = false;
}
+static inline uint8_t* get_xfer_buffer(void) {
+ // Use EP0 buffer if it is large enough, otherwise use dedicated buffer
+ #if CFG_TUD_DFU_XFER_BUFSIZE > CFG_TUD_ENDPOINT0_BUFSIZE
+ return _transfer_buf;
+ #else
+ return usbd_get_ctrl_buf();
+ #endif
+}
+
static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
@@ -253,6 +260,8 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
tud_dfu_detach_cb();
+ } else {
+ // nothing to do
}
break;
@@ -275,6 +284,8 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
tud_dfu_abort_cb(_dfu_ctx.alt);
+ } else {
+ // nothing to do
}
break;
@@ -283,10 +294,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
- const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
+ const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, get_xfer_buffer(),
request->wLength);
- return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, xfer_len);
+ return tud_control_xfer(rhport, request, get_xfer_buffer(), xfer_len);
}
break;
@@ -303,10 +314,10 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
_dfu_ctx.block = request->wValue;
_dfu_ctx.length = request->wLength;
- if (request->wLength) {
+ if (request->wLength > 0) {
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, request->wLength);
+ return tud_control_xfer(rhport, request, get_xfer_buffer(), request->wLength);
} else {
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
@@ -327,7 +338,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
default:
if (stage == CONTROL_STAGE_SETUP) {
- return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ return reply_getstatus(rhport, request, (dfu_state_t) _dfu_ctx.state, (dfu_status_t) _dfu_ctx.status, 0);
}
break;
}
@@ -352,6 +363,8 @@ void tud_dfu_finish_flashing(uint8_t status) {
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
? DFU_MANIFEST_SYNC
: DFU_MANIFEST_WAIT_RESET;
+ } else {
+ // nothing to do
}
} else {
// failed while flashing, move to dfuError
@@ -374,14 +387,16 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_DNBUSY;
- tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_epbuf.transfer_buf, _dfu_ctx.length);
+ tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, get_xfer_buffer(), _dfu_ctx.length);
} else {
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
+ } else {
+ // nothing to do
}
return true;
@@ -401,7 +416,7 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_MANIFEST;
@@ -409,6 +424,8 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus
} else {
_dfu_ctx.state = DFU_IDLE;
}
+ } else {
+ // nothing to do
}
return true;
diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h
index e59e61ce9..b22b4c450 100644
--- a/src/class/dfu/dfu_device.h
+++ b/src/class/dfu/dfu_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_DEVICE_H_
-#define _TUSB_DFU_DEVICE_H_
+#ifndef TUSB_DFU_DEVICE_H_
+#define TUSB_DFU_DEVICE_H_
#include "dfu.h"
@@ -96,4 +96,4 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_r
}
#endif
-#endif /* _TUSB_DFU_MODE_DEVICE_H_ */
+#endif /* TUSB_DFU_MODE_DEVICE_H_ */
diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h
index 67eb26d95..c4116d8fe 100644
--- a/src/class/dfu/dfu_rt_device.h
+++ b/src/class/dfu/dfu_rt_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_RT_DEVICE_H_
-#define _TUSB_DFU_RT_DEVICE_H_
+#ifndef TUSB_DFU_RT_DEVICE_H_
+#define TUSB_DFU_RT_DEVICE_H_
#include "dfu.h"
@@ -52,4 +52,4 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_req
}
#endif
-#endif /* _TUSB_DFU_RT_DEVICE_H_ */
+#endif /* TUSB_DFU_RT_DEVICE_H_ */
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index b69f623a0..0883d95ac 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -28,8 +28,8 @@
* \defgroup ClassDriver_HID Human Interface Device (HID)
* @{ */
-#ifndef _TUSB_HID_H_
-#define _TUSB_HID_H_
+#ifndef TUSB_HID_H_
+#define TUSB_HID_H_
#include "common/tusb_common.h"
@@ -2071,6 +2071,6 @@ enum {
}
#endif
-#endif /* _TUSB_HID_H__ */
+#endif /* TUSB_HID_H__ */
/// @}
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index b4f24902b..6ee4cd9c1 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -128,7 +128,7 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, u
TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin, CFG_TUD_HID_EP_BUFSIZE, report, len));
}
- return usbd_edpt_xfer(rhport, p_hid->ep_in, p_epbuf->epin, len);
+ return usbd_edpt_xfer(rhport, p_hid->ep_in, p_epbuf->epin, len, false);
}
uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
@@ -263,7 +263,7 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16
// Prepare for output endpoint
if (p_hid->ep_out) {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE), drv_len);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE, false), drv_len);
}
return drv_len;
@@ -413,7 +413,7 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
// prepare for new transfer
- TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE));
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE, false));
}
return true;
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index fc1dbcbd8..32b572973 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -267,14 +267,14 @@ void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, u
// Stylus Pen Report Descriptor Template
#define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \
- HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \
+ HID_USAGE ( HID_USAGE_DIGITIZER_PEN ) , \
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \
/* Report ID if any */\
__VA_ARGS__ \
- HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \
- HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \
- HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \
- HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \
+ HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ), \
+ HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), \
+ HID_USAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ), \
+ HID_USAGE ( HID_USAGE_DIGITIZER_IN_RANGE ), \
HID_LOGICAL_MIN ( 0 ), \
HID_LOGICAL_MAX ( 1 ), \
HID_REPORT_SIZE ( 1 ), \
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index da776d04c..fc7704258 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -74,44 +74,31 @@ static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len) {
- (void) dev_addr;
- (void) idx;
- (void) report_desc;
- (void) desc_len;
+ (void) dev_addr; (void) idx; (void) report_desc; (void) desc_len;
}
TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) {
- (void) dev_addr;
- (void) idx;
+ (void) dev_addr; (void) idx;
+}
+
+TU_ATTR_WEAK void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len) {
+ (void) dev_addr; (void) idx; (void) report; (void) len;
}
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len) {
- (void) dev_addr;
- (void) idx;
- (void) report;
- (void) len;
+ (void) dev_addr; (void) idx; (void) report; (void) len;
}
TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {
- (void) dev_addr;
- (void) idx;
- (void) report_id;
- (void) report_type;
- (void) len;
+ (void) dev_addr; (void) idx; (void) report_id; (void) report_type; (void) len;
}
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {
- (void) dev_addr;
- (void) idx;
- (void) report_id;
- (void) report_type;
- (void) len;
+ (void) dev_addr; (void) idx; (void) report_id; (void) report_type; (void) len;
}
TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
- (void) dev_addr;
- (void) idx;
- (void) protocol;
+ (void) dev_addr; (void) idx; (void) protocol;
}
//--------------------------------------------------------------------+
@@ -141,7 +128,9 @@ static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
static hidh_interface_t* find_new_itf(void) {
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
- if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
+ if (_hidh_itf[i].daddr == 0) {
+ return &_hidh_itf[i];
+ }
}
return NULL;
}
@@ -152,7 +141,9 @@ static hidh_interface_t* find_new_itf(void) {
uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
uint8_t count = 0;
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
- if (_hidh_itf[i].daddr == daddr) count++;
+ if (_hidh_itf[i].daddr == daddr) {
+ count++;
+ }
}
return count;
}
@@ -160,7 +151,9 @@ uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
uint8_t tuh_hid_itf_get_total_count(void) {
uint8_t count = 0;
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
- if (_hidh_itf[i].daddr != 0) count++;
+ if (_hidh_itf[i].daddr != 0) {
+ count++;
+ }
}
return count;
}
@@ -234,7 +227,7 @@ void tuh_hid_set_default_protocol(uint8_t protocol) {
_hidh_default_protocol = protocol;
}
-static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
+static bool hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
@@ -266,7 +259,7 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
- return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
+ return hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
}
static void get_report_complete(tuh_xfer_t* xfer) {
@@ -353,7 +346,7 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r
return tuh_control_xfer(&xfer);
}
-static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate,
+static bool hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
// SET IDLE request, device can stall if not support this request
TU_LOG_DRV("HID Set Idle \r\n");
@@ -506,36 +499,43 @@ void hidh_close(uint8_t daddr) {
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
-
-bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
+uint16_t hidh_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
(void) max_len;
- TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
+ TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
// len = interface + hid + n*endpoints
- uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
- desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
- TU_ASSERT(max_len >= drv_len);
- uint8_t const* p_desc = (uint8_t const*) desc_itf;
+ const uint16_t drv_len = (uint16_t)(sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
+ desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_ASSERT(drv_len <= max_len, 0);
+ const uint8_t *p_desc = (const uint8_t *)desc_itf;
- //------------- HID descriptor -------------//
+ // HID descriptor: mostly right after interface descriptor, in some rare case it might be after endpoint descriptors
p_desc = tu_desc_next(p_desc);
- tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
- TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
-
- hidh_interface_t* p_hid = find_new_itf();
- TU_ASSERT(p_hid); // not enough interface, try to increase CFG_TUH_HID
- p_hid->daddr = daddr;
+ const tusb_hid_descriptor_hid_t *desc_hid;
+ if (tu_desc_type(p_desc) == HID_DESC_TYPE_HID) {
+ // HID after interface
+ desc_hid = (const tusb_hid_descriptor_hid_t *)p_desc;
+ p_desc = tu_desc_next(p_desc);
+ } else {
+ // HID after endpoint
+ desc_hid = (const tusb_hid_descriptor_hid_t *)(p_desc + sizeof(tusb_desc_endpoint_t) * desc_itf->bNumEndpoints);
+ TU_ASSERT(tu_desc_type(desc_hid) == HID_DESC_TYPE_HID, 0);
+ }
- //------------- Endpoint Descriptors -------------//
- p_desc = tu_desc_next(p_desc);
- tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
+ // Allocate new interface
+ hidh_interface_t *p_hid = find_new_itf();
+ TU_ASSERT(p_hid, 0); // not enough interface, try to increase CFG_TUH_HID
+ p_hid->daddr = daddr;
+ p_hid->itf_num = desc_itf->bInterfaceNumber;
- for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
- TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
- TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ // Endpoint Descriptors
+ for (uint8_t i = 0; i < desc_itf->bNumEndpoints; i++) {
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType, 0);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep), 0);
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
p_hid->ep_in = desc_ep->bEndpointAddress;
@@ -546,23 +546,20 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_
}
p_desc = tu_desc_next(p_desc);
- desc_ep = (tusb_desc_endpoint_t const*) p_desc;
}
- p_hid->itf_num = desc_itf->bInterfaceNumber;
-
// Assume bNumDescriptors = 1
p_hid->report_desc_type = desc_hid->bReportType;
// Use offsetof to avoid pointer to the odd/misaligned address
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*)desc_hid + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
- // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
- p_hid->protocol_mode = _hidh_default_protocol;
+ // Per HID Specs: default is Report protocol
+ p_hid->protocol_mode = HID_PROTOCOL_REPORT;
if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) {
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
}
- return true;
+ return drv_len;
}
//--------------------------------------------------------------------+
@@ -613,25 +610,30 @@ static void process_set_config(tuh_xfer_t* xfer) {
switch (state) {
case CONFG_SET_IDLE: {
- // Idle rate = 0 mean only report when there is changes
+ // Idle rate = 0 mean only report when there are changes
const uint16_t idle_rate = 0;
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE)
? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
- _hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
+ hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
break;
}
case CONFIG_SET_PROTOCOL:
- _hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
+ #if CFG_TUH_HID_SET_PROTOCOL_ON_ENUM
+ hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
case CONFIG_GET_REPORT_DESC:
+ if (xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL && xfer->result == XFER_RESULT_SUCCESS) {
+ p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ }
// Get Report Descriptor if possible
- // using usbh enumeration buffer since report descriptor can be very long
+ // using usbh enumeration buffer since the report descriptor can be very long
if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) {
TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
-
- // Driver is mounted without report descriptor
config_driver_mount_complete(daddr, idx, NULL, 0);
} else {
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0,
@@ -641,8 +643,8 @@ static void process_set_config(tuh_xfer_t* xfer) {
break;
case CONFIG_COMPLETE: {
- uint8_t const* desc_report = usbh_get_enum_buf();
- uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
+ const uint8_t *desc_report = usbh_get_enum_buf();
+ const uint16_t desc_len = tu_le16toh(xfer->setup->wLength);
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
break;
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index 032827af1..95ba859ad 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_HID_HOST_H_
-#define _TUSB_HID_HOST_H_
+#ifndef TUSB_HID_HOST_H_
+#define TUSB_HID_HOST_H_
#include "hid.h"
@@ -36,31 +36,32 @@ extern "C" {
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
-
// TODO Highspeed interrupt can be up to 512 bytes
#ifndef CFG_TUH_HID_EPIN_BUFSIZE
-#define CFG_TUH_HID_EPIN_BUFSIZE 64
+ #define CFG_TUH_HID_EPIN_BUFSIZE 64
#endif
#ifndef CFG_TUH_HID_EPOUT_BUFSIZE
-#define CFG_TUH_HID_EPOUT_BUFSIZE 64
+ #define CFG_TUH_HID_EPOUT_BUFSIZE 64
#endif
+#ifndef CFG_TUH_HID_SET_PROTOCOL_ON_ENUM
+ #define CFG_TUH_HID_SET_PROTOCOL_ON_ENUM 1
+#endif
+//--------------------------------------------------------------------+
+// Interface API
+//--------------------------------------------------------------------+
typedef struct {
- uint8_t report_id;
- uint8_t usage;
+ uint8_t report_id;
+ uint8_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
-// uint8_t in_len; // length of IN report
-// uint8_t out_len; // length of OUT report
+ // uint8_t in_len; // length of IN report
+ // uint8_t out_len; // length of OUT report
} tuh_hid_report_info_t;
-//--------------------------------------------------------------------+
-// Interface API
-//--------------------------------------------------------------------+
-
// Get the total number of mounted HID interfaces of a device
uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
@@ -68,10 +69,10 @@ uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
uint8_t tuh_hid_itf_get_total_count(void);
// backward compatible rename
-#define tuh_hid_instance_count tuh_hid_itf_get_count
+#define tuh_hid_instance_count tuh_hid_itf_get_count
// Get Interface information
-bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* itf_info);
+bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t *itf_info);
// Get Interface index from device address + interface number
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
@@ -85,8 +86,8 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
-TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
- uint8_t const* desc_report, uint16_t desc_len);
+TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t *reports_info_arr, uint8_t arr_count,
+ const uint8_t *desc_report, uint16_t desc_len);
//--------------------------------------------------------------------+
// Control Endpoint API
@@ -107,12 +108,13 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
// Get Report using control endpoint
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
-bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
+bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void *report,
+ uint16_t len);
// Set Report using control endpoint
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
-bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
- void* report, uint16_t len);
+bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void *report,
+ uint16_t len);
//--------------------------------------------------------------------+
// Interrupt Endpoint API
@@ -133,11 +135,12 @@ bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
// Send report using interrupt endpoint
-// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
-bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len);
+// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is
+// sent.
+bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void *report, uint16_t len);
//--------------------------------------------------------------------+
-// Callbacks (Weak is optional)
+// Callbacks (optional)
//--------------------------------------------------------------------+
// Invoked when device with hid interface is mounted
@@ -145,25 +148,27 @@ bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
-void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len);
+void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report_desc, uint16_t desc_len);
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx);
// Invoked when received report from device via interrupt endpoint
// Note: if there is report ID (composite), it is 1st byte of report
-void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
+void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len);
// Invoked when sent report to device successfully via interrupt endpoint
-void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
+void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, const uint8_t *report, uint16_t len);
// Invoked when Get Report to device via either control endpoint
// len = 0 indicate there is error in the transfer e.g stalled response
-void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
+void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
+ uint16_t len);
// Invoked when Sent Report to device via either control endpoint
// len = 0 indicate there is error in the transfer e.g stalled response
-void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
+void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
+ uint16_t len);
// Invoked when Set Protocol request is complete
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
@@ -171,15 +176,15 @@ void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t pro
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-bool hidh_init(void);
-bool hidh_deinit(void);
-bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
-bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
-bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-void hidh_close(uint8_t dev_addr);
+bool hidh_init(void);
+bool hidh_deinit(void);
+uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
+bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void hidh_close(uint8_t dev_addr);
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_HID_HOST_H_ */
+#endif /* TUSB_HID_HOST_H_ */
diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h
index cd67640e4..8121ec016 100644
--- a/src/class/midi/midi.h
+++ b/src/class/midi/midi.h
@@ -186,6 +186,24 @@ typedef midi_desc_cs_endpoint_n_t(1) midi_desc_cs_endpoint_1jack_t;
TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not correct");
//--------------------------------------------------------------------+
+// MIDI 2.0 UMP Helpers
+//--------------------------------------------------------------------+
+
+// Return the number of 32-bit words for a UMP message given its Message Type
+static inline uint8_t midi2_ump_word_count(uint8_t mt) {
+ switch (mt) {
+ case 0x0: case 0x1: case 0x2: case 0x6: case 0x7:
+ return 1;
+ case 0x3: case 0x4: case 0x8: case 0x9: case 0xA:
+ return 2;
+ case 0xB: case 0xC:
+ return 3;
+ default: // 0x5, 0xD, 0xE, 0xF
+ return 4;
+ }
+}
+
+//--------------------------------------------------------------------+
// For Internal Driver Use
//--------------------------------------------------------------------+
typedef struct {
diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c
new file mode 100644
index 000000000..b14f439f8
--- /dev/null
+++ b/src/class/midi/midi2_device.c
@@ -0,0 +1,769 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && CFG_TUD_MIDI2
+
+#include <string.h>
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+#include "midi2_device.h"
+
+//--------------------------------------------------------------------+
+// Weak stubs
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_midi2_rx_cb(uint8_t itf) { (void) itf; }
+TU_ATTR_WEAK void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt) { (void) itf; (void) alt; }
+TU_ATTR_WEAK bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_request_t* request) {
+ (void) rhport; (void) request; return false;
+}
+TU_ATTR_WEAK uint8_t tud_midi2_num_groups_cb(uint8_t itf) {
+ (void) itf; return CFG_TUD_MIDI2_NUM_GROUPS;
+}
+TU_ATTR_WEAK uint8_t tud_midi2_num_function_blocks_cb(uint8_t itf) {
+ (void) itf; return CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS;
+}
+TU_ATTR_WEAK const char* tud_midi2_ep_name_cb(uint8_t itf) {
+ (void) itf; return CFG_TUD_MIDI2_EP_NAME;
+}
+TU_ATTR_WEAK const char* tud_midi2_product_id_cb(uint8_t itf) {
+ (void) itf; return CFG_TUD_MIDI2_PRODUCT_ID;
+}
+
+//--------------------------------------------------------------------+
+// Byte order note
+//--------------------------------------------------------------------+
+// Per USB-MIDI 2.0 Section 3.2.2, each 32-bit UMP word is transmitted with the
+// least significant byte first. This driver reads and writes UMP words as
+// native uint32_t through tu_edpt_stream_read/write. All TinyUSB targets are
+// little-endian, so the in-memory layout already matches the wire order and no
+// swap is needed. If a big-endian target is ever supported, wrap access with
+// tu_htole32 / tu_le32toh at the buffer boundary.
+
+//--------------------------------------------------------------------+
+// UMP Stream Message Constants
+//--------------------------------------------------------------------+
+// UMP Message Type for Stream messages (bits 31:28)
+enum {
+ MT_STREAM = 0x0F,
+};
+
+// UMP Stream Status values (10-bit, bits 25:16)
+enum {
+ STREAM_ENDPOINT_DISCOVERY = 0x000,
+ STREAM_ENDPOINT_INFO = 0x001,
+ STREAM_EP_NAME = 0x003,
+ STREAM_PROD_INSTANCE_ID = 0x004,
+ STREAM_CONFIG_REQUEST = 0x005,
+ STREAM_CONFIG_NOTIFY = 0x006,
+ STREAM_FB_DISCOVERY = 0x010,
+ STREAM_FB_INFO = 0x011,
+};
+
+enum {
+ UMP_VER_MAJOR = 1,
+ UMP_VER_MINOR = 1,
+};
+
+// Group Terminal Block descriptor types (USB-MIDI 2.0)
+enum {
+ MIDI2_CS_GRP_TRM_BLOCK = 0x26,
+ MIDI2_GRP_TRM_BLOCK_HEADER = 0x01,
+ MIDI2_GRP_TRM_BLOCK_ENTRY = 0x02,
+};
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct {
+ uint8_t ep_addr;
+ uint16_t mps;
+ tu_fifo_t ff;
+
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+ uint8_t* ep_buf;
+#endif
+} midi2d_tx_t;
+
+typedef struct {
+ uint8_t rhport;
+ uint8_t itf_num;
+ uint8_t alt_setting;
+ uint8_t protocol;
+ bool negotiated;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ struct {
+ midi2d_tx_t tx;
+ tu_edpt_stream_t rx;
+
+ uint8_t rx_ff_buf[CFG_TUD_MIDI2_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_MIDI2_TX_BUFSIZE];
+ } ep_stream;
+} midi2d_interface_t;
+
+// Skip local EP buffer if dedicated hw FIFO is supported
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+typedef struct {
+ TUD_EPBUF_DEF(epin, CFG_TUD_MIDI2_TX_EPSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_MIDI2_RX_EPSIZE);
+} midi2d_epbuf_t;
+
+CFG_TUD_MEM_SECTION static midi2d_epbuf_t _midi2d_epbuf[CFG_TUD_MIDI2];
+#endif
+
+TU_VERIFY_STATIC(CFG_TUD_MIDI2_NUM_GROUPS >= 1 && CFG_TUD_MIDI2_NUM_GROUPS <= 16,
+ "CFG_TUD_MIDI2_NUM_GROUPS must be 1..16");
+TU_VERIFY_STATIC(CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS >= 1 && CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS <= 32,
+ "CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS must be 1..32");
+
+#define ITF_MEM_RESET_SIZE offsetof(midi2d_interface_t, ep_stream)
+
+static midi2d_interface_t _midi2d_itf[CFG_TUD_MIDI2];
+
+// Default Group Terminal Block descriptor (USB-MIDI 2.0 spec, Table 5-5/5-6)
+static const uint8_t _default_gtb_desc[] = {
+ // GTB Header (5 bytes)
+ 5, // bLength
+ MIDI2_CS_GRP_TRM_BLOCK, // bDescriptorType
+ MIDI2_GRP_TRM_BLOCK_HEADER, // bDescriptorSubtype
+ U16_TO_U8S_LE(18), // wTotalLength (5 + 13 = 18)
+
+ // GTB Entry (13 bytes)
+ 13, // bLength
+ MIDI2_CS_GRP_TRM_BLOCK, // bDescriptorType
+ MIDI2_GRP_TRM_BLOCK_ENTRY, // bDescriptorSubtype
+ 1, // bGrpTrmBlkID
+ 0x00, // bGrpTrmBlkType: bidirectional
+ 0x00, // nGroupTrm: first group (0)
+ CFG_TUD_MIDI2_NUM_GROUPS, // nNumGroupTrm
+ CFG_TUD_MIDI2_BLOCK_STRIDX, // iBlockItem: string descriptor index (0 = none)
+ 0x00, // bMIDIProtocol: unknown/not fixed
+ 0, 0, // wMaxInputBandwidth: unknown
+ 0, 0 // wMaxOutputBandwidth: unknown
+};
+
+//--------------------------------------------------------------------+
+// Common utility functions
+//--------------------------------------------------------------------+
+
+static inline uint8_t _itf_idx(const midi2d_interface_t* p_midi) {
+ return (uint8_t)(p_midi - _midi2d_itf);
+}
+
+static inline bool _tx_opened(const midi2d_interface_t* p_midi) {
+ return p_midi->ep_stream.tx.ep_addr != 0;
+}
+
+static uint8_t _tx_byte_at(const tu_fifo_buffer_info_t* info, uint16_t offset) {
+ if (offset < info->linear.len) {
+ return info->linear.ptr[offset];
+ }
+
+ offset = (uint16_t) (offset - info->linear.len);
+ if (offset < info->wrapped.len) {
+ return info->wrapped.ptr[offset];
+ }
+
+ return 0;
+}
+
+// Calculate the largest byte count that contains only whole UMP packets and
+// fits in one USB transfer (<= mps).
+static uint16_t _tx_nonseg_len_to_mps(midi2d_tx_t* tx) {
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_read_info(&tx->ff, &info);
+
+ const uint16_t available = (uint16_t) (info.linear.len + info.wrapped.len);
+ uint16_t bytes = 0;
+
+ while (bytes < tx->mps) {
+ if ((uint16_t) (available - bytes) < 4) break;
+
+ uint8_t mt = (uint8_t)((_tx_byte_at(&info, (uint16_t) (bytes + 3)) >> 4) & 0x0F);
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint16_t pkt_bytes = (uint16_t) pkt_words * 4;
+
+ if (pkt_bytes == 0) break;
+ if ((uint16_t) (available - bytes) < pkt_bytes) break;
+ if ((uint16_t) (bytes + pkt_bytes) > tx->mps) break;
+
+ bytes = (uint16_t) (bytes + pkt_bytes);
+ }
+
+ return bytes;
+}
+
+// Start one IN transfer capped at mps, return number of bytes queued to the controller, or 0 if nothing was queued.
+static uint16_t _tx_start_xfer(midi2d_interface_t* p_midi) {
+ midi2d_tx_t* tx = &p_midi->ep_stream.tx;
+ uint16_t ff_count = tu_fifo_count(&tx->ff);
+
+ if (ff_count == 0) return 0;
+
+ if (!usbd_edpt_claim(p_midi->rhport, tx->ep_addr)) return 0;
+
+ uint16_t bytes;
+ if (p_midi->alt_setting == 1) {
+ bytes = _tx_nonseg_len_to_mps(tx);
+ } else {
+ bytes = tu_min16(tu_fifo_count(&tx->ff), tx->mps);
+ }
+ if (bytes == 0) {
+ usbd_edpt_release(p_midi->rhport, tx->ep_addr);
+ return 0;
+ }
+
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ TU_ASSERT(usbd_edpt_xfer_fifo(p_midi->rhport, tx->ep_addr, &tx->ff, bytes, false), 0);
+#else
+ tu_fifo_read_n(&tx->ff, tx->ep_buf, bytes);
+ TU_ASSERT(usbd_edpt_xfer(p_midi->rhport, tx->ep_addr, tx->ep_buf, bytes, false), 0);
+#endif
+
+ return bytes;
+}
+
+static uint32_t _tx_ump_write(midi2d_interface_t* p_midi, const uint32_t* words, uint32_t count) {
+ uint32_t written = 0;
+ while (written < count) {
+ uint8_t mt = (uint8_t)((words[written] >> 28) & 0x0F);
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint16_t pkt_bytes = (uint16_t) pkt_words * 4;
+
+ if (written + pkt_words > count) break;
+ if (tu_fifo_remaining(&p_midi->ep_stream.tx.ff) < pkt_bytes) break;
+
+ if (tu_fifo_write_n(&p_midi->ep_stream.tx.ff, &words[written], pkt_bytes) != pkt_bytes) break;
+ written += pkt_words;
+ }
+
+ (void) _tx_start_xfer(p_midi);
+ return written;
+}
+
+//--------------------------------------------------------------------+
+// Protocol Negotiation
+//--------------------------------------------------------------------+
+static void _nego_send_ump(midi2d_interface_t* p_midi, const uint32_t* words, uint8_t count) {
+ if (!_tx_opened(p_midi)) return;
+ if (tu_fifo_remaining(&p_midi->ep_stream.tx.ff) < (uint32_t) count * 4) return;
+ (void) _tx_ump_write(p_midi, words, count);
+}
+
+static void _nego_send_endpoint_info(midi2d_interface_t* p_midi) {
+ uint32_t msg[4] = {0};
+ msg[0] = ((uint32_t) MT_STREAM << 28)
+ | ((uint32_t) STREAM_ENDPOINT_INFO << 16)
+ | ((uint32_t) UMP_VER_MAJOR << 8)
+ | (uint32_t) UMP_VER_MINOR;
+ msg[1] = (UINT32_C(1) << 31) // Static Function Blocks flag
+ | ((uint32_t)(tud_midi2_num_function_blocks_cb(_itf_idx(p_midi)) & 0x7F) << 24)
+ | (UINT32_C(1) << 9) // MIDI 2.0 Protocol capability
+ | (UINT32_C(1) << 8); // MIDI 1.0 Protocol capability
+ _nego_send_ump(p_midi, msg, 4);
+}
+
+static void _nego_send_stream_text(midi2d_interface_t* p_midi, uint16_t status, const char* str) {
+ if (!str || str[0] == '\0') return;
+
+ uint16_t total_len = (uint16_t) strlen(str);
+ uint16_t offset = 0;
+
+ while (offset < total_len) {
+ uint16_t remaining = total_len - offset;
+ uint8_t n = (uint8_t)((remaining > 14) ? 14 : remaining);
+ bool is_first = (offset == 0);
+ bool is_last = (remaining <= 14);
+
+ uint8_t form;
+ if (is_first && is_last) form = 0;
+ else if (is_first) form = 1;
+ else if (is_last) form = 3;
+ else form = 2;
+
+ uint32_t msg[4] = {0};
+ msg[0] = ((uint32_t) MT_STREAM << 28)
+ | ((uint32_t) form << 26)
+ | ((uint32_t) status << 16);
+
+ const char* p = str + offset;
+ if (n > 0) msg[0] |= ((uint32_t)(uint8_t) p[0] << 8);
+ if (n > 1) msg[0] |= (uint32_t)(uint8_t) p[1];
+ for (uint8_t i = 2; i < n; i++) {
+ uint8_t word_idx = (uint8_t)(1 + (i - 2) / 4);
+ uint8_t shift = (uint8_t)(24 - ((i - 2) % 4) * 8);
+ msg[word_idx] |= ((uint32_t)(uint8_t) p[i] << shift);
+ }
+
+ _nego_send_ump(p_midi, msg, 4);
+ offset += n;
+ }
+}
+
+static void _nego_send_config_notify(midi2d_interface_t* p_midi, uint8_t protocol) {
+ uint32_t msg[4] = {0};
+ msg[0] = ((uint32_t) MT_STREAM << 28)
+ | ((uint32_t) STREAM_CONFIG_NOTIFY << 16)
+ | ((uint32_t) protocol << 8);
+ _nego_send_ump(p_midi, msg, 4);
+}
+
+static void _nego_send_fb_info(midi2d_interface_t* p_midi, uint8_t fb_idx) {
+ uint32_t msg[4] = {0};
+ msg[0] = ((uint32_t) MT_STREAM << 28)
+ | ((uint32_t) STREAM_FB_INFO << 16)
+ | (UINT32_C(1) << 15)
+ | ((uint32_t) fb_idx << 8)
+ | 0x02; // bDirection: bidirectional
+ msg[1] = ((uint32_t) 0 << 24) // bFirstGroup
+ | ((uint32_t) tud_midi2_num_groups_cb(_itf_idx(p_midi)) << 16);
+ _nego_send_ump(p_midi, msg, 4);
+}
+
+static void _nego_handle_stream_msg(midi2d_interface_t* p_midi, const uint32_t* words) {
+ uint16_t status = (words[0] >> 16) & 0x3FF;
+
+ switch (status) {
+ case STREAM_ENDPOINT_DISCOVERY:
+ _nego_send_endpoint_info(p_midi);
+ _nego_send_stream_text(p_midi, STREAM_EP_NAME, tud_midi2_ep_name_cb(_itf_idx(p_midi)));
+ _nego_send_stream_text(p_midi, STREAM_PROD_INSTANCE_ID, tud_midi2_product_id_cb(_itf_idx(p_midi)));
+ break;
+
+ case STREAM_CONFIG_REQUEST: {
+ uint8_t req_proto = (words[0] >> 8) & 0xFF;
+ if (req_proto == MIDI_PROTOCOL_MIDI1 || req_proto == MIDI_PROTOCOL_MIDI2) {
+ p_midi->protocol = req_proto;
+ }
+ _nego_send_config_notify(p_midi, p_midi->protocol);
+ p_midi->negotiated = true;
+ break;
+ }
+
+ case STREAM_FB_DISCOVERY: {
+ uint8_t fb_idx = (words[0] >> 8) & 0xFF;
+ uint8_t fb_count = tud_midi2_num_function_blocks_cb(_itf_idx(p_midi));
+ if (fb_idx == 0xFF) {
+ for (uint8_t f = 0; f < fb_count; f++) {
+ _nego_send_fb_info(p_midi, f);
+ }
+ } else if (fb_idx < fb_count) {
+ _nego_send_fb_info(p_midi, fb_idx);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+static void _nego_process_rx(midi2d_interface_t* p_midi) {
+ tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx;
+ uint8_t word_bytes[4];
+
+ while (tu_fifo_peek_n(&ep_rx->ff, word_bytes, 4) == 4) {
+ // UMP words travel LSB-first on the wire and in LE memory, so MT is in
+ // the high nibble of byte 3, not byte 0.
+ uint8_t mt = (word_bytes[3] >> 4) & 0x0F;
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint32_t pkt_bytes = (uint32_t)pkt_words * 4;
+
+ if (mt != MT_STREAM) break;
+ if (tu_edpt_stream_read_available(ep_rx) < pkt_bytes) break;
+
+ uint32_t buf[4] = {0};
+ tu_edpt_stream_read(ep_rx, buf, pkt_bytes);
+ _nego_handle_stream_msg(p_midi, buf);
+ }
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+bool tud_midi2_n_mounted(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2, false);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+ return _tx_opened(p_midi) &&
+ tu_edpt_stream_is_opened(&p_midi->ep_stream.rx);
+}
+
+uint32_t tud_midi2_n_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+ return tu_edpt_stream_read_available(&p_midi->ep_stream.rx) / 4;
+}
+
+uint32_t tud_midi2_n_ump_read(uint8_t itf, uint32_t* words, uint32_t max_words) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2 && words != NULL && max_words > 0, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+
+ // UMP API is only valid on Alt Setting 1 (USB-MIDI 2.0).
+ // Alt 0 carries USB-MIDI 1.0 32-bit Event Packets, not UMP words.
+ if (p_midi->alt_setting != 1) { return 0; }
+
+ tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx;
+
+ uint32_t total_read = 0;
+ while (total_read < max_words) {
+ uint8_t word_bytes[4];
+ if (tu_fifo_peek_n(&ep_rx->ff, word_bytes, 4) < 4) break;
+
+ // UMP words travel LSB-first; MT is the high nibble of byte 3, not byte 0.
+ uint8_t mt = (word_bytes[3] >> 4) & 0x0F;
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+
+ if (total_read + pkt_words > max_words) break;
+ if (tu_edpt_stream_read_available(ep_rx) < (uint32_t)pkt_words * 4) break;
+
+ tu_edpt_stream_read(ep_rx, &words[total_read], pkt_words * 4);
+ total_read += pkt_words;
+ }
+
+ return total_read;
+}
+
+uint32_t tud_midi2_n_packet_read(uint8_t itf, uint8_t packets[], uint32_t max_packets) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2 && packets != NULL && max_packets > 0, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+ return tu_edpt_stream_read(&p_midi->ep_stream.rx, packets, max_packets * 4u) >> 2u;
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_midi2_n_ump_write(uint8_t itf, const uint32_t* words, uint32_t count) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2 && words != NULL && count > 0, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+
+ // UMP API is only valid on Alt Setting 1 (USB-MIDI 2.0).
+ // Alt 0 carries USB-MIDI 1.0 32-bit Event Packets, not UMP words.
+ if (p_midi->alt_setting != 1) { return 0; }
+ TU_VERIFY(_tx_opened(p_midi), 0);
+
+ return _tx_ump_write(p_midi, words, count);
+}
+
+uint32_t tud_midi2_n_packet_write(uint8_t itf, const uint8_t packets[], uint32_t count) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2 && packets != NULL && count > 0, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[itf];
+ midi2d_tx_t* tx = &p_midi->ep_stream.tx;
+
+ // Packet API is for Alt Setting 0 (USB-MIDI 1.0) event packets.
+ TU_VERIFY(p_midi->alt_setting == 0, 0);
+ TU_VERIFY(_tx_opened(p_midi), 0);
+
+ uint32_t written = 0;
+ while (written < count) {
+ if (tu_fifo_remaining(&tx->ff) < 4) break;
+
+ if (tu_fifo_write_n(&tx->ff, packets + written * 4u, 4) != 4) break;
+ written++;
+ }
+
+ (void) _tx_start_xfer(p_midi);
+
+ return written;
+}
+
+//--------------------------------------------------------------------+
+// STATE GETTERS
+//--------------------------------------------------------------------+
+uint8_t tud_midi2_n_alt_setting(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2, 0);
+ return _midi2d_itf[itf].alt_setting;
+}
+
+bool tud_midi2_n_negotiated(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2, false);
+ return _midi2d_itf[itf].negotiated;
+}
+
+uint8_t tud_midi2_n_protocol(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_MIDI2, 0);
+ return _midi2d_itf[itf].protocol;
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void midi2d_init(void) {
+ tu_memclr(_midi2d_itf, sizeof(_midi2d_itf));
+ for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) {
+ midi2d_interface_t* p_midi = &_midi2d_itf[i];
+ p_midi->protocol = MIDI_PROTOCOL_MIDI2;
+
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ uint8_t *epout_buf = _midi2d_epbuf[i].epout;
+ uint8_t *epin_buf = _midi2d_epbuf[i].epin;
+ #endif
+
+ tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false,
+ p_midi->ep_stream.rx_ff_buf, CFG_TUD_MIDI2_RX_BUFSIZE, epout_buf);
+
+ midi2d_tx_t* tx = &p_midi->ep_stream.tx;
+ (void) tu_fifo_config(&tx->ff, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI2_TX_BUFSIZE, false);
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+ tx->ep_buf = epin_buf;
+#else
+ (void) epin_buf;
+#endif
+ }
+}
+
+bool midi2d_deinit(void) {
+ for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) {
+ midi2d_interface_t* p_midi = &_midi2d_itf[i];
+ tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
+ }
+ return true;
+}
+
+void midi2d_reset(uint8_t rhport) {
+ (void) rhport;
+ for (uint8_t i = 0; i < CFG_TUD_MIDI2; i++) {
+ midi2d_interface_t* p_midi = &_midi2d_itf[i];
+ tu_memclr(p_midi, ITF_MEM_RESET_SIZE);
+
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ tu_edpt_stream_close(&p_midi->ep_stream.rx);
+
+ tu_fifo_clear(&p_midi->ep_stream.tx.ff);
+ p_midi->ep_stream.tx.ep_addr = 0;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_midi2_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_MIDI2; idx++) {
+ const midi2d_interface_t* p_midi = &_midi2d_itf[idx];
+ if (ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+static uint8_t find_midi2_itf_by_num(uint8_t itf_num) {
+ for (uint8_t idx = 0; idx < CFG_TUD_MIDI2; idx++) {
+ if (_midi2d_itf[idx].itf_num == itf_num) return idx;
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+uint16_t midi2d_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
+ const uint8_t* p_desc = (const uint8_t*) desc_itf;
+ const uint8_t* desc_end = p_desc + max_len;
+
+ // 1st Interface: Audio Control v1 (optional)
+ if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
+ p_desc = tu_desc_next(desc_itf);
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+
+ // 2nd Interface: MIDI Streaming
+ TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+ const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
+
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
+ AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol,
+ 0);
+
+ uint8_t idx = find_midi2_itf(0);
+ TU_ASSERT(idx < CFG_TUD_MIDI2, 0);
+ midi2d_interface_t* p_midi = &_midi2d_itf[idx];
+
+ p_midi->rhport = rhport;
+ p_midi->itf_num = desc_midi->bInterfaceNumber;
+ p_midi->alt_setting = 0;
+ p_midi->protocol = MIDI_PROTOCOL_MIDI2;
+ p_midi->negotiated = false;
+
+ p_desc = tu_desc_next(p_desc);
+
+ // Skip class-specific descriptors
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Find and open endpoint descriptors
+ uint8_t found_ep = 0;
+ while ((found_ep < desc_midi->bNumEndpoints) && tu_desc_in_bounds(p_desc, desc_end)) {
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
+ const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ const uint8_t ep_addr = desc_ep->bEndpointAddress;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ p_midi->ep_stream.tx.ep_addr = ep_addr;
+ p_midi->ep_stream.tx.mps = tu_edpt_packet_size(desc_ep);
+ tu_fifo_clear(&p_midi->ep_stream.tx.ff);
+ } else {
+ tu_edpt_stream_open(&p_midi->ep_stream.rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep));
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ TU_ASSERT(tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx) > 0, 0);
+ }
+
+ found_ep++;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Skip remaining descriptors (alt setting 1, CS endpoints, GTB)
+ // Stop at any interface descriptor that is not our MIDI Streaming alt setting
+ while (tu_desc_in_bounds(p_desc, desc_end)) {
+ uint8_t dtype = tu_desc_type(p_desc);
+
+ if (dtype == TUSB_DESC_INTERFACE) {
+ const tusb_desc_interface_t* next_itf = (const tusb_desc_interface_t*) p_desc;
+ // Continue only if this is an alternate setting of our own interface
+ if (next_itf->bInterfaceNumber != desc_midi->bInterfaceNumber) break;
+ } else if (dtype != TUSB_DESC_CS_INTERFACE && dtype != TUSB_DESC_CS_ENDPOINT &&
+ dtype != TUSB_DESC_ENDPOINT) {
+ break;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ return (uint16_t)(p_desc - (const uint8_t*) desc_itf);
+}
+
+bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ TU_LOG2("MIDI2 ctrl: stage=%u bRequest=0x%02X wValue=0x%04X wIndex=0x%04X wLength=%u\r\n",
+ stage, request->bRequest, request->wValue, request->wIndex, request->wLength);
+
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
+ switch (request->bRequest) {
+ case TUSB_REQ_SET_INTERFACE: {
+ uint8_t itf_num = tu_u16_low(request->wIndex);
+ uint8_t alt = tu_u16_low(request->wValue);
+
+ // Only Alt Setting 0 (MIDI 1.0) and 1 (UMP) are valid
+ if (alt > 1) return false;
+
+ uint8_t idx = find_midi2_itf_by_num(itf_num);
+ if (idx >= CFG_TUD_MIDI2) return false;
+
+ midi2d_interface_t* p_midi = &_midi2d_itf[idx];
+ p_midi->alt_setting = alt;
+
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ tu_fifo_clear(&p_midi->ep_stream.tx.ff);
+
+ if (alt == 1) {
+ p_midi->negotiated = false;
+ p_midi->protocol = MIDI_PROTOCOL_MIDI2;
+ }
+
+ // Re-arm RX endpoint for receiving data after alt setting change
+ tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx);
+
+ tud_midi2_set_itf_cb(idx, alt);
+ tud_control_status(rhport, request);
+ return true;
+ }
+
+ case TUSB_REQ_GET_DESCRIPTOR: {
+ // USB-MIDI 2.0 Section 6: GTB descriptor retrieval
+ // bmRequestType = 0x81 (Device-to-Host, Standard, Interface)
+ // wValue = CS_GR_TRM_BLOCK (0x26) in high byte, alt setting in low byte
+ // wIndex = interface number
+ if (request->bmRequestType_bit.direction != TUSB_DIR_IN) return false;
+ if (request->bmRequestType_bit.type != TUSB_REQ_TYPE_STANDARD) return false;
+ if (request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE) return false;
+ if (tu_u16_high(request->wValue) != MIDI2_CS_GRP_TRM_BLOCK) return false;
+
+ uint8_t itf_num = tu_u16_low(request->wIndex);
+ uint8_t idx = find_midi2_itf_by_num(itf_num);
+ if (idx >= CFG_TUD_MIDI2) return false;
+
+ // Only Alt Setting 1 exposes Group Terminal Block descriptors.
+ if (tu_u16_low(request->wValue) != 0x01) return false;
+
+ if (tud_midi2_get_req_itf_cb(rhport, request)) return true;
+
+ uint16_t len = request->wLength;
+ if (len > sizeof(_default_gtb_desc)) {
+ len = sizeof(_default_gtb_desc);
+ }
+ tud_control_xfer(rhport, request, (void*)(uintptr_t) _default_gtb_desc, len);
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+bool midi2d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) rhport;
+
+ uint8_t idx = find_midi2_itf(ep_addr);
+ TU_ASSERT(idx < CFG_TUD_MIDI2);
+ midi2d_interface_t* p_midi = &_midi2d_itf[idx];
+
+ tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx;
+ midi2d_tx_t* ep_tx = &p_midi->ep_stream.tx;
+
+ if (ep_addr == ep_rx->ep_addr) {
+ if (result == XFER_RESULT_SUCCESS) {
+ tu_edpt_stream_read_xfer_complete(ep_rx, xferred_bytes);
+ if (p_midi->alt_setting == 1) {
+ _nego_process_rx(p_midi);
+ }
+ tud_midi2_rx_cb(idx);
+ }
+ tu_edpt_stream_read_xfer(ep_rx);
+ } else if (ep_addr == ep_tx->ep_addr && result == XFER_RESULT_SUCCESS) {
+ uint16_t queued = _tx_start_xfer(p_midi);
+ // Send ZLP if no more data is queued but the last transfer was exactly mps
+ if (queued == 0 && tu_fifo_count(&ep_tx->ff) == 0 && xferred_bytes > 0 &&
+ (0 == (xferred_bytes & (ep_tx->mps - 1)))) {
+ if (usbd_edpt_claim(rhport, ep_tx->ep_addr)) {
+ usbd_edpt_xfer(rhport, ep_tx->ep_addr, NULL, 0, false);
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+#endif
diff --git a/src/class/midi/midi2_device.h b/src/class/midi/midi2_device.h
new file mode 100644
index 000000000..e53535693
--- /dev/null
+++ b/src/class/midi/midi2_device.h
@@ -0,0 +1,193 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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_MIDI2_DEVICE_H_
+#define TUSB_MIDI2_DEVICE_H_
+
+#include "class/audio/audio.h"
+#include "midi.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// Config defaults are in tusb_option.h:
+// CFG_TUD_MIDI2_RX_EPSIZE, CFG_TUD_MIDI2_TX_EPSIZE,
+// CFG_TUD_MIDI2_RX_BUFSIZE, CFG_TUD_MIDI2_TX_BUFSIZE,
+// CFG_TUD_MIDI2_NUM_GROUPS, CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS,
+// CFG_TUD_MIDI2_EP_NAME, CFG_TUD_MIDI2_PRODUCT_ID
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUD_MIDI2_TX_EPSIZE
+ #define CFG_TUD_MIDI2_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+#ifndef CFG_TUD_MIDI2_RX_EPSIZE
+ #define CFG_TUD_MIDI2_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+#ifndef CFG_TUD_MIDI2_TX_BUFSIZE
+ #define CFG_TUD_MIDI2_TX_BUFSIZE CFG_TUD_MIDI2_TX_EPSIZE
+#endif
+
+#ifndef CFG_TUD_MIDI2_RX_BUFSIZE
+ #define CFG_TUD_MIDI2_RX_BUFSIZE CFG_TUD_MIDI2_RX_EPSIZE
+#endif
+
+#ifndef CFG_TUD_MIDI2_NUM_GROUPS
+ #define CFG_TUD_MIDI2_NUM_GROUPS 1
+#endif
+
+#ifndef CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS
+ #define CFG_TUD_MIDI2_NUM_FUNCTION_BLOCKS 1
+#endif
+
+#ifndef CFG_TUD_MIDI2_EP_NAME
+ #define CFG_TUD_MIDI2_EP_NAME "TinyUSB MIDI 2.0"
+#endif
+
+#ifndef CFG_TUD_MIDI2_PRODUCT_ID
+ #define CFG_TUD_MIDI2_PRODUCT_ID "TinyUSB-MIDI2"
+#endif
+
+// String descriptor index for the Group Terminal Block (iBlockItem, Table 5-6).
+// 0 = no string descriptor (default, spec-allowed).
+#ifndef CFG_TUD_MIDI2_BLOCK_STRIDX
+ #define CFG_TUD_MIDI2_BLOCK_STRIDX 0
+#endif
+
+//--------------------------------------------------------------------+
+// MIDI Protocol Values (returned by tud_midi2_n_protocol)
+//--------------------------------------------------------------------+
+
+// Per USB-MIDI 2.0 spec, UMP Stream Configuration messages.
+enum {
+ MIDI_PROTOCOL_MIDI1 = 0x01,
+ MIDI_PROTOCOL_MIDI2 = 0x02,
+};
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak, optional)
+//--------------------------------------------------------------------+
+void tud_midi2_rx_cb(uint8_t itf);
+void tud_midi2_set_itf_cb(uint8_t itf, uint8_t alt);
+bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_request_t* request);
+
+// Per-interface UMP Stream config (override for per-itf values).
+uint8_t tud_midi2_num_groups_cb(uint8_t itf);
+uint8_t tud_midi2_num_function_blocks_cb(uint8_t itf);
+const char* tud_midi2_ep_name_cb(uint8_t itf);
+const char* tud_midi2_product_id_cb(uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+//--------------------------------------------------------------------+
+
+bool tud_midi2_n_mounted(uint8_t itf);
+uint32_t tud_midi2_n_available(uint8_t itf);
+uint8_t tud_midi2_n_alt_setting(uint8_t itf);
+bool tud_midi2_n_negotiated(uint8_t itf);
+uint8_t tud_midi2_n_protocol(uint8_t itf);
+
+// Read up to max_words UMP words from the RX FIFO. Returns the number of
+// words actually read (0 if FIFO is empty).
+//
+// NOTE: this function returns when max_words is reached or when the FIFO is
+// empty, whichever comes first. Applications should invoke it in a loop
+// until it returns 0 to guarantee the RX FIFO is fully drained per
+// tud_midi2_rx_cb callback. Leaving words in the FIFO across callbacks can
+// prevent subsequent bulk OUT transfers from landing.
+uint32_t tud_midi2_n_ump_read(uint8_t itf, uint32_t* words, uint32_t max_words);
+uint32_t tud_midi2_n_ump_write(uint8_t itf, const uint32_t* words, uint32_t count);
+
+uint32_t tud_midi2_n_packet_read(uint8_t itf, uint8_t packets[], uint32_t max_packets);
+uint32_t tud_midi2_n_packet_write(uint8_t itf, const uint8_t packets[], uint32_t count);
+
+//--------------------------------------------------------------------+
+// Application API (Single Interface)
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_mounted(void) {
+ return tud_midi2_n_mounted(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi2_available(void) {
+ return tud_midi2_n_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_midi2_alt_setting(void) {
+ return tud_midi2_n_alt_setting(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi2_negotiated(void) {
+ return tud_midi2_n_negotiated(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_midi2_protocol(void) {
+ return tud_midi2_n_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi2_ump_read(uint32_t* words, uint32_t max_words) {
+ return tud_midi2_n_ump_read(0, words, max_words);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi2_ump_write(const uint32_t* words, uint32_t count) {
+ return tud_midi2_n_ump_write(0, words, count);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi2_packet_read(uint8_t packets[], uint32_t max_packets) {
+ return tud_midi2_n_packet_read(0, packets, max_packets);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi2_packet_write(const uint8_t packets[], uint32_t count) {
+ return tud_midi2_n_packet_write(0, packets, count);
+}
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void midi2d_init(void);
+bool midi2d_deinit(void);
+void midi2d_reset(uint8_t rhport);
+uint16_t midi2d_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len);
+bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
+bool midi2d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/class/midi/midi2_host.c b/src/class/midi/midi2_host.c
new file mode 100644
index 000000000..6d8861f4b
--- /dev/null
+++ b/src/class/midi/midi2_host.c
@@ -0,0 +1,635 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+#include "tusb_option.h"
+
+#if (CFG_TUH_ENABLED && CFG_TUH_MIDI2)
+
+#include "host/usbh.h"
+#include "host/usbh_pvt.h"
+#include "midi2_host.h"
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MIDI2_LOG_LEVEL, __VA_ARGS__)
+
+//--------------------------------------------------------------------+
+// Weak stubs for application callbacks
+//--------------------------------------------------------------------+
+
+TU_ATTR_WEAK void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t *desc_cb_data) {
+ (void) idx; (void) desc_cb_data;
+}
+
+TU_ATTR_WEAK void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t *mount_cb_data) {
+ (void) idx; (void) mount_cb_data;
+}
+
+TU_ATTR_WEAK void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void) idx; (void) xferred_bytes;
+}
+
+TU_ATTR_WEAK void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void) idx; (void) xferred_bytes;
+}
+
+TU_ATTR_WEAK void tuh_midi2_umount_cb(uint8_t idx) {
+ (void) idx;
+}
+
+//--------------------------------------------------------------------+
+// Internal structure and state
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t ep_addr;
+ uint16_t mps;
+ tu_fifo_t ff;
+ uint8_t* ep_buf;
+} midih2_tx_t;
+
+typedef struct {
+ uint8_t daddr;
+ uint8_t bInterfaceNumber;
+
+ uint8_t alt_setting_current;
+
+ uint8_t protocol_version;
+ uint8_t bcdMSC_hi, bcdMSC_lo;
+ uint8_t rx_cable_count_alt0;
+ uint8_t tx_cable_count_alt0;
+ uint8_t rx_cable_count_alt1;
+ uint8_t tx_cable_count_alt1;
+
+ struct {
+ midih2_tx_t tx;
+ tu_edpt_stream_t rx;
+
+ uint8_t rx_ff_buf[CFG_TUH_MIDI2_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUH_MIDI2_TX_BUFSIZE];
+ } ep_stream;
+
+ bool mounted;
+} midih2_interface_t;
+
+static midih2_interface_t _midi2_host[CFG_TUH_MIDI2];
+
+typedef struct {
+ TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MAX);
+ TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MAX);
+} midih2_epbuf_t;
+
+CFG_TUH_MEM_SECTION static midih2_epbuf_t _midi2_epbuf[CFG_TUH_MIDI2];
+
+//--------------------------------------------------------------------+
+// Helper functions
+//--------------------------------------------------------------------+
+
+static inline uint8_t find_new_midi2_index(void) {
+ for (uint8_t idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ if (_midi2_host[idx].daddr == 0) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ const midih2_interface_t *p_midi = &_midi2_host[idx];
+ if ((p_midi->daddr == daddr) &&
+ (ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr)) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+static inline bool _tuh_tx_opened(const midih2_interface_t* p_midi) {
+ return p_midi->ep_stream.tx.ep_addr != 0;
+}
+
+static uint8_t _tuh_tx_byte_at(const tu_fifo_buffer_info_t* info, uint16_t offset) {
+ if (offset < info->linear.len) {
+ return info->linear.ptr[offset];
+ }
+ offset = (uint16_t)(offset - info->linear.len);
+ if (offset < info->wrapped.len) {
+ return info->wrapped.ptr[offset];
+ }
+ return 0;
+}
+
+// Largest byte count containing only whole UMP packets and fitting one xfer.
+static uint16_t _tuh_tx_nonseg_len_to_mps(midih2_tx_t* tx) {
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_read_info(&tx->ff, &info);
+
+ const uint16_t available = (uint16_t)(info.linear.len + info.wrapped.len);
+ uint16_t bytes = 0;
+
+ while (bytes < tx->mps) {
+ if ((uint16_t)(available - bytes) < 4) break;
+
+ uint8_t mt = (uint8_t)((_tuh_tx_byte_at(&info, (uint16_t)(bytes + 3)) >> 4) & 0x0F);
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint16_t pkt_bytes = (uint16_t)(pkt_words * 4);
+
+ if (pkt_bytes == 0) break;
+ if ((uint16_t)(available - bytes) < pkt_bytes) break;
+ if ((uint16_t)(bytes + pkt_bytes) > tx->mps) break;
+
+ bytes = (uint16_t)(bytes + pkt_bytes);
+ }
+
+ return bytes;
+}
+
+// Start one OUT transfer capped at mps. Returns bytes queued, or 0 if nothing.
+static uint16_t _tuh_tx_start_xfer(midih2_interface_t* p_midi) {
+ midih2_tx_t* tx = &p_midi->ep_stream.tx;
+ uint16_t ff_count = tu_fifo_count(&tx->ff);
+ if (ff_count == 0) return 0;
+ if (!usbh_edpt_claim(p_midi->daddr, tx->ep_addr)) return 0;
+
+ uint16_t bytes;
+ if (p_midi->alt_setting_current == 1) {
+ bytes = _tuh_tx_nonseg_len_to_mps(tx);
+ } else {
+ bytes = tu_min16(tu_fifo_count(&tx->ff), tx->mps);
+ }
+ if (bytes == 0) {
+ usbh_edpt_release(p_midi->daddr, tx->ep_addr);
+ return 0;
+ }
+
+ tu_fifo_read_n(&tx->ff, tx->ep_buf, bytes);
+ TU_ASSERT(usbh_edpt_xfer(p_midi->daddr, tx->ep_addr, tx->ep_buf, bytes), 0);
+ return bytes;
+}
+
+static uint32_t _tuh_tx_ump_write(midih2_interface_t* p_midi, const uint32_t* words, uint32_t count) {
+ uint32_t written = 0;
+ while (written < count) {
+ uint8_t mt = (uint8_t)((words[written] >> 28) & 0x0F);
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint16_t pkt_bytes = (uint16_t)(pkt_words * 4);
+
+ if (written + pkt_words > count) break;
+ if (tu_fifo_remaining(&p_midi->ep_stream.tx.ff) < pkt_bytes) break;
+ if (tu_fifo_write_n(&p_midi->ep_stream.tx.ff, &words[written], pkt_bytes) != pkt_bytes) break;
+ written += pkt_words;
+ }
+
+ (void) _tuh_tx_start_xfer(p_midi);
+ return written;
+}
+
+//--------------------------------------------------------------------+
+// Descriptor parsing
+//--------------------------------------------------------------------+
+
+// Parse Alt Setting 0 (MIDI 1.0) descriptors. Returns pointer past last consumed descriptor.
+static const uint8_t* midih2_parse_descriptors_alt0(midih2_interface_t *p_midi,
+ const tusb_desc_interface_t *desc_itf, const uint8_t *desc_end) {
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, NULL);
+
+ p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
+
+ const uint8_t *p_desc = (const uint8_t *) desc_itf;
+ p_desc = tu_desc_next(p_desc);
+
+ uint8_t rx_cable_count = 0;
+ uint8_t tx_cable_count = 0;
+ bool found_new_interface = false;
+
+ while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) {
+ switch (tu_desc_type(p_desc)) {
+ case TUSB_DESC_INTERFACE:
+ found_new_interface = true;
+ break;
+
+ case TUSB_DESC_ENDPOINT: {
+ const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
+
+ TU_ASSERT(tuh_edpt_open(p_midi->daddr, p_ep), NULL);
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_open(&p_midi->ep_stream.rx, p_midi->daddr, p_ep, tu_edpt_packet_size(p_ep));
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ } else {
+ p_midi->ep_stream.tx.ep_addr = p_ep->bEndpointAddress;
+ p_midi->ep_stream.tx.mps = tu_edpt_packet_size(p_ep);
+ tu_fifo_clear(&p_midi->ep_stream.tx.ff);
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
+ const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
+ tx_cable_count = p_csep->bNumEmbMIDIJack;
+ } else {
+ rx_cable_count = p_csep->bNumEmbMIDIJack;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!found_new_interface) {
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+
+ p_midi->rx_cable_count_alt0 = rx_cable_count;
+ p_midi->tx_cable_count_alt0 = tx_cable_count;
+ return p_desc;
+}
+
+// Parse Alt Setting 1 (MIDI 2.0 UMP) descriptors. Returns pointer past last consumed descriptor.
+static const uint8_t* midih2_parse_descriptors_alt1(midih2_interface_t *p_midi,
+ const tusb_desc_interface_t *desc_itf, const uint8_t *desc_end) {
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, NULL);
+ TU_VERIFY(desc_itf->bAlternateSetting == 1, NULL);
+
+ const uint8_t *p_desc = (const uint8_t *) desc_itf;
+ p_desc = tu_desc_next(p_desc);
+
+ uint8_t rx_cable_count = 0;
+ uint8_t tx_cable_count = 0;
+ bool found_new_interface = false;
+
+ while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) {
+ switch (tu_desc_type(p_desc)) {
+ case TUSB_DESC_INTERFACE:
+ found_new_interface = true;
+ break;
+
+ case TUSB_DESC_CS_INTERFACE:
+ if (tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_HEADER) {
+ // bcdMSC at offset 3-4 in CS Interface Header
+ const uint8_t *bcd_ptr = p_desc + 3;
+ p_midi->bcdMSC_lo = bcd_ptr[0];
+ p_midi->bcdMSC_hi = bcd_ptr[1];
+ if (p_midi->bcdMSC_hi == 0x02) { // bcdMSC 0x0200 = USB-MIDI 2.0
+ p_midi->protocol_version = 1;
+ }
+ }
+ break;
+
+ case TUSB_DESC_ENDPOINT: {
+ const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
+ p_desc = tu_desc_next(p_desc);
+
+ if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
+ // MIDI 2.0 CS Endpoint General 2.0: bNumGrpTrmBlk at offset 3
+ if (p_desc[0] >= 4 && p_desc[2] == MIDI_CS_ENDPOINT_GENERAL_2_0) {
+ uint8_t num_grp_trm_blk = p_desc[3];
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
+ tx_cable_count = num_grp_trm_blk;
+ } else {
+ rx_cable_count = num_grp_trm_blk;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!found_new_interface) {
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+
+ p_midi->rx_cable_count_alt1 = rx_cable_count;
+ p_midi->tx_cable_count_alt1 = tx_cable_count;
+ return p_desc;
+}
+
+//--------------------------------------------------------------------+
+// Auto-selection logic
+//--------------------------------------------------------------------+
+
+static void midih2_auto_select_alt_setting(midih2_interface_t *p_midi) {
+ p_midi->alt_setting_current = 0;
+ if (p_midi->protocol_version == 1) {
+ p_midi->alt_setting_current = 1;
+ }
+}
+
+//--------------------------------------------------------------------+
+// Init/Deinit
+//--------------------------------------------------------------------+
+
+bool midih2_init(void) {
+ tu_memclr(&_midi2_host, sizeof(_midi2_host));
+ for (int inst = 0; inst < CFG_TUH_MIDI2; inst++) {
+ midih2_interface_t *p_midi = &_midi2_host[inst];
+
+ tu_edpt_stream_init(&p_midi->ep_stream.rx, true, false, false,
+ p_midi->ep_stream.rx_ff_buf, CFG_TUH_MIDI2_RX_BUFSIZE, _midi2_epbuf[inst].rx);
+
+ // TX uses raw tu_fifo + direct usbh_edpt_xfer (no FIFO wrapper) to preserve
+ // UMP packet boundaries across USB transfers.
+ midih2_tx_t* tx = &p_midi->ep_stream.tx;
+ (void) tu_fifo_config(&tx->ff, p_midi->ep_stream.tx_ff_buf, CFG_TUH_MIDI2_TX_BUFSIZE, false);
+ tx->ep_buf = _midi2_epbuf[inst].tx;
+ }
+ return true;
+}
+
+bool midih2_deinit(void) {
+ for (size_t i = 0; i < CFG_TUH_MIDI2; i++) {
+ midih2_interface_t* p_midi = &_midi2_host[i];
+ tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Class driver callbacks
+//--------------------------------------------------------------------+
+
+uint16_t midih2_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
+ (void) rhport;
+
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
+
+ // For Alt Setting 1, reuse existing slot for same device+interface
+ uint8_t idx = TUSB_INDEX_INVALID_8;
+ if (desc_itf->bAlternateSetting > 0) {
+ for (uint8_t i = 0; i < CFG_TUH_MIDI2; i++) {
+ if (_midi2_host[i].daddr == dev_addr &&
+ _midi2_host[i].bInterfaceNumber == desc_itf->bInterfaceNumber) {
+ idx = i;
+ break;
+ }
+ }
+ }
+ if (idx == TUSB_INDEX_INVALID_8) {
+ idx = find_new_midi2_index();
+ }
+ TU_VERIFY(idx < CFG_TUH_MIDI2, 0);
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+ p_midi->daddr = dev_addr;
+
+ const uint8_t *desc_start = (const uint8_t *) desc_itf;
+ const uint8_t *desc_end = desc_start + max_len;
+
+ // Skip Audio Control interface and any non-MIDI-Streaming descriptors
+ // (following midi_host.c pattern from Ha Thach)
+ if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
+ const uint8_t *p_desc = tu_desc_next((const uint8_t *)desc_itf);
+ // Skip CS_INTERFACE header
+ TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE, 0);
+ p_desc = tu_desc_next(p_desc);
+ desc_itf = (const tusb_desc_interface_t *) p_desc;
+ // Skip until we find MIDI Streaming interface
+ while (tu_desc_in_bounds(p_desc, desc_end) &&
+ (desc_itf->bDescriptorType != TUSB_DESC_INTERFACE ||
+ (desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO &&
+ desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
+ p_desc = tu_desc_next(p_desc);
+ desc_itf = (const tusb_desc_interface_t *) p_desc;
+ }
+ TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0);
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
+ }
+
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, 0);
+
+ TU_LOG_DRV("MIDI2 opening Interface %u Alt %u (addr = %u)\r\n",
+ desc_itf->bInterfaceNumber, desc_itf->bAlternateSetting, dev_addr);
+
+ // Dispatch to appropriate parser based on Alt Setting
+ const uint8_t *p_end = NULL;
+ if (desc_itf->bAlternateSetting == 0) {
+ p_end = midih2_parse_descriptors_alt0(p_midi, desc_itf, desc_end);
+ } else if (desc_itf->bAlternateSetting == 1) {
+ p_end = midih2_parse_descriptors_alt1(p_midi, desc_itf, desc_end);
+ }
+
+ // Return number of bytes consumed (following midi_host.c pattern)
+ uint16_t const parsed_len = (p_end != NULL) ? (uint16_t)(p_end - desc_start) : 0;
+ return parsed_len;
+}
+
+static void midih2_set_config_complete(midih2_interface_t *p_midi, uint8_t idx) {
+ uint8_t dev_addr = p_midi->daddr;
+
+ // Invoke descriptor_cb
+ tuh_midi2_descriptor_cb_t desc_cb = {
+ .protocol_version = p_midi->protocol_version,
+ .bcdMSC_hi = p_midi->bcdMSC_hi,
+ .bcdMSC_lo = p_midi->bcdMSC_lo,
+ .rx_cable_count = (p_midi->alt_setting_current == 0) ?
+ p_midi->rx_cable_count_alt0 : p_midi->rx_cable_count_alt1,
+ .tx_cable_count = (p_midi->alt_setting_current == 0) ?
+ p_midi->tx_cable_count_alt0 : p_midi->tx_cable_count_alt1,
+ };
+ tuh_midi2_descriptor_cb(idx, &desc_cb);
+
+ // Mark as mounted
+ TU_LOG_DRV("MIDI2 mounted addr = %u, alt = %u, protocol = %u\r\n",
+ dev_addr, p_midi->alt_setting_current, p_midi->protocol_version);
+ p_midi->mounted = true;
+
+ // Invoke mount_cb
+ tuh_midi2_mount_cb_t mount_cb = {
+ .daddr = dev_addr,
+ .bInterfaceNumber = p_midi->bInterfaceNumber,
+ .protocol_version = p_midi->protocol_version,
+ .alt_setting_active = p_midi->alt_setting_current,
+ .rx_cable_count = desc_cb.rx_cable_count,
+ .tx_cable_count = desc_cb.tx_cable_count,
+ };
+ tuh_midi2_mount_cb(idx, &mount_cb);
+
+ // Prepare RX transfer
+ tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx);
+
+ // Signal USBH that configuration is complete
+ usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
+}
+
+static void midih2_set_interface_cb(tuh_xfer_t *xfer) {
+ uint8_t const dev_addr = xfer->daddr;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+
+ // Find our interface
+ for (uint8_t idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ if (_midi2_host[idx].daddr == dev_addr && _midi2_host[idx].bInterfaceNumber == itf_num) {
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ midih2_set_config_complete(&_midi2_host[idx], idx);
+ } else {
+ // SET_INTERFACE failed, fall back to alt 0
+ TU_LOG_DRV("MIDI2 SET_INTERFACE failed, falling back to alt 0\r\n");
+ _midi2_host[idx].alt_setting_current = 0;
+ midih2_set_config_complete(&_midi2_host[idx], idx);
+ }
+ return;
+ }
+ }
+}
+
+bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num) {
+ uint8_t idx = 0;
+ for (idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ if (_midi2_host[idx].daddr == dev_addr && _midi2_host[idx].bInterfaceNumber == itf_num) {
+ break;
+ }
+ }
+
+ if (idx >= CFG_TUH_MIDI2) {
+ // Not our interface (e.g. Audio Control) - pass through to next
+ usbh_driver_set_config_complete(dev_addr, itf_num);
+ return true;
+ }
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+
+ // Auto-select alt setting
+ midih2_auto_select_alt_setting(p_midi);
+
+ // If MIDI 2.0 detected, issue SET_INTERFACE to activate Alt Setting 1
+ if (p_midi->alt_setting_current == 1) {
+ TU_LOG_DRV("MIDI2 requesting SET_INTERFACE alt 1 for itf %u\r\n", itf_num);
+ TU_ASSERT(tuh_interface_set(dev_addr, itf_num, 1, midih2_set_interface_cb, 0));
+ } else {
+ // MIDI 1.0 only, complete immediately
+ midih2_set_config_complete(p_midi, idx);
+ }
+
+ return true;
+}
+
+void midih2_close(uint8_t dev_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+ if (p_midi->daddr == dev_addr) {
+ TU_LOG_DRV(" MIDI2 close addr = %u index = %u\r\n", dev_addr, idx);
+ tu_edpt_stream_close(&p_midi->ep_stream.rx);
+ tu_fifo_clear(&p_midi->ep_stream.tx.ff);
+ p_midi->ep_stream.tx.ep_addr = 0;
+ tuh_midi2_umount_cb(idx);
+ tu_memclr(p_midi, sizeof(midih2_interface_t));
+ }
+ }
+}
+
+bool midih2_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+ midih2_tx_t* ep_tx = &p_midi->ep_stream.tx;
+
+ if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
+ if (result == XFER_RESULT_SUCCESS && xferred_bytes > 0) {
+ tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
+ tuh_midi2_rx_cb(idx, xferred_bytes);
+ }
+ tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx);
+ } else if (ep_addr == ep_tx->ep_addr) {
+ tuh_midi2_tx_cb(idx, xferred_bytes);
+ if (result == XFER_RESULT_SUCCESS) {
+ uint16_t queued = _tuh_tx_start_xfer(p_midi);
+ // Send ZLP if no more data is queued but the last transfer was exactly mps
+ if (queued == 0 && tu_fifo_count(&ep_tx->ff) == 0 && xferred_bytes > 0 &&
+ (0 == (xferred_bytes & (ep_tx->mps - 1)))) {
+ if (usbh_edpt_claim(dev_addr, ep_tx->ep_addr)) {
+ usbh_edpt_xfer(dev_addr, ep_tx->ep_addr, NULL, 0);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Public API
+//--------------------------------------------------------------------+
+
+bool tuh_midi2_mounted(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+ return _midi2_host[idx].mounted;
+}
+
+uint8_t tuh_midi2_get_protocol_version(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+ return _midi2_host[idx].protocol_version;
+}
+
+uint8_t tuh_midi2_get_alt_setting_active(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+ return _midi2_host[idx].alt_setting_current;
+}
+
+uint8_t tuh_midi2_get_cable_count(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+ return (_midi2_host[idx].alt_setting_current == 0) ?
+ _midi2_host[idx].rx_cable_count_alt0 : _midi2_host[idx].rx_cable_count_alt1;
+}
+
+uint32_t tuh_midi2_ump_read(uint8_t idx, uint32_t* words, uint32_t max_words) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2 && words && max_words);
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+ tu_edpt_stream_t *ep_rx = &p_midi->ep_stream.rx;
+
+ uint32_t n_words = 0;
+ for (uint32_t i = 0; i < max_words; i++) {
+ if (tu_edpt_stream_read_available(ep_rx) >= 4) {
+ tu_edpt_stream_read(ep_rx, (uint8_t *) &words[i], 4);
+ n_words++;
+ } else {
+ break;
+ }
+ }
+
+ return n_words;
+}
+
+uint32_t tuh_midi2_ump_write(uint8_t idx, const uint32_t* words, uint32_t count) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2 && words && count);
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+ TU_VERIFY(_tuh_tx_opened(p_midi), 0);
+
+ return _tuh_tx_ump_write(p_midi, words, count);
+}
+
+uint32_t tuh_midi2_write_flush(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUH_MIDI2);
+ return _tuh_tx_start_xfer(&_midi2_host[idx]);
+}
+
+#endif // CFG_TUH_ENABLED && CFG_TUH_MIDI2
diff --git a/src/class/midi/midi2_host.h b/src/class/midi/midi2_host.h
new file mode 100644
index 000000000..47039eb85
--- /dev/null
+++ b/src/class/midi/midi2_host.h
@@ -0,0 +1,107 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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_MIDI2_HOST_H_
+#define TUSB_MIDI2_HOST_H_
+
+#include "class/audio/audio.h"
+#include "midi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Callback Type Definitions
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t protocol_version; // 0 = MIDI 1.0 only, 1 = MIDI 2.0
+ uint8_t bcdMSC_hi, bcdMSC_lo; // MIDI version from descriptor
+ uint8_t rx_cable_count; // For both alt settings (same for Alt 0 and Alt 1)
+ uint8_t tx_cable_count;
+} tuh_midi2_descriptor_cb_t;
+
+typedef struct {
+ uint8_t daddr;
+ uint8_t bInterfaceNumber;
+ uint8_t protocol_version; // 0 = MIDI 1.0, 1 = MIDI 2.0
+ uint8_t alt_setting_active; // 0 or 1
+ uint8_t rx_cable_count;
+ uint8_t tx_cable_count;
+} tuh_midi2_mount_cb_t;
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak, optional)
+//--------------------------------------------------------------------+
+
+void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t *desc_cb_data);
+void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t *mount_cb_data);
+void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes);
+void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes);
+void tuh_midi2_umount_cb(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Application API - Query
+//--------------------------------------------------------------------+
+
+bool tuh_midi2_mounted(uint8_t idx);
+uint8_t tuh_midi2_get_protocol_version(uint8_t idx);
+uint8_t tuh_midi2_get_alt_setting_active(uint8_t idx);
+uint8_t tuh_midi2_get_cable_count(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Application API - I/O
+//--------------------------------------------------------------------+
+
+// Read up to max_words UMP words from the RX FIFO. Returns the number of
+// words actually read (0 if FIFO is empty).
+//
+// NOTE: this function returns when max_words is reached or when the FIFO is
+// empty, whichever comes first. Applications should invoke it in a loop
+// until it returns 0 to guarantee the RX FIFO is fully drained per
+// tuh_midi2_rx_cb callback. Leaving words in the FIFO across callbacks can
+// prevent subsequent bulk IN transfers from landing.
+uint32_t tuh_midi2_ump_read(uint8_t idx, uint32_t* words, uint32_t max_words);
+uint32_t tuh_midi2_ump_write(uint8_t idx, const uint32_t* words, uint32_t count);
+uint32_t tuh_midi2_write_flush(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+
+bool midih2_init(void);
+bool midih2_deinit(void);
+bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num);
+void midih2_close(uint8_t dev_addr);
+uint16_t midih2_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
+bool midih2_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 7dac7c4a5..de4ff5dd8 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -26,7 +26,7 @@
#include "tusb_option.h"
-#if (CFG_TUD_ENABLED && CFG_TUD_MIDI)
+#if CFG_TUD_ENABLED && CFG_TUD_MIDI
//--------------------------------------------------------------------+
// INCLUDE
@@ -37,12 +37,18 @@
#include "midi_device.h"
//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf) {
+ (void)itf;
+}
+
+//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
+ uint8_t rhport;
uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
// For Stream read()/write() API
// Messages are always 4 bytes long, queue them for reading and writing so the
@@ -51,134 +57,98 @@ typedef struct {
midi_driver_stream_t stream_read;
/*------------- From this point, data is not cleared by bus reset -------------*/
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
- uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
+ // Endpoint stream
+ struct {
+ tu_edpt_stream_t tx;
+ tu_edpt_stream_t rx;
- #if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
- #endif
+ uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
+ } ep_stream;
} midid_interface_t;
-#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
+#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, ep_stream)
-// Endpoint Transfer buffer
-CFG_TUD_MEM_SECTION static struct {
- TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
- TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
-} _midid_epbuf[CFG_TUD_MIDI];
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
static midid_interface_t _midid_itf[CFG_TUD_MIDI];
-bool tud_midi_n_mounted (uint8_t itf) {
- midid_interface_t* midi = &_midid_itf[itf];
- return midi->ep_in && midi->ep_out;
-}
-
-static void _prep_out_transaction(uint8_t idx) {
- const uint8_t rhport = 0;
- midid_interface_t* p_midi = &_midid_itf[idx];
- uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
-
- // Prepare for incoming data but only allow what we can store in the ring buffer.
- // 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 >= CFG_TUD_MIDI_EP_BUFSIZE, );
-
- // claim endpoint
- TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
-
- // fifo can be changed before endpoint is claimed
- available = tu_fifo_remaining(&p_midi->rx_ff);
-
- if ( available >= CFG_TUD_MIDI_EP_BUFSIZE ) {
- usbd_edpt_xfer(rhport, p_midi->ep_out, _midid_epbuf[idx].epout, CFG_TUD_MIDI_EP_BUFSIZE);
- }else
- {
- // Release endpoint since we don't make any transfer
- usbd_edpt_release(rhport, p_midi->ep_out);
- }
-}
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+// Endpoint Transfer buffer: not used if dedicated hw FIFO is available
+typedef struct {
+ TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_TX_EPSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_RX_EPSIZE);
+} midid_epbuf_t;
+CFG_TUD_MEM_SECTION static midid_epbuf_t _midid_epbuf[CFG_TUD_MIDI];
+ #endif
//--------------------------------------------------------------------+
-// Weak stubs: invoked if no strong implementation is available
+// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf) {
- (void) itf;
+bool tud_midi_n_mounted (uint8_t itf) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ const bool tx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.tx);
+ const bool rx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.rx);
+ return tx_opened && rx_opened;
}
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
-uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
-{
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num) {
(void) cable_num;
-
- midid_interface_t* midi = &_midid_itf[itf];
- const midi_driver_stream_t* stream = &midi->stream_read;
+ const midid_interface_t *p_midi = &_midid_itf[itf];
+ const midi_driver_stream_t *stream = &p_midi->stream_read;
+ const tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
// when using with packet API stream total & index are both zero
- return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
+ return tu_edpt_stream_read_available(ep_str) + (uint8_t)(stream->total - stream->index);
}
-uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
-{
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize) {
(void) cable_num;
- TU_VERIFY(bufsize, 0);
-
- uint8_t* buf8 = (uint8_t*) buffer;
+ TU_VERIFY(buffer != NULL && bufsize > 0, 0);
- midid_interface_t* midi = &_midid_itf[itf];
- midi_driver_stream_t* stream = &midi->stream_read;
+ uint8_t *buf8 = (uint8_t *)buffer;
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_read;
uint32_t total_read = 0;
- while( bufsize )
- {
+ while (bufsize > 0) {
// Get new packet from fifo, then set packet expected bytes
- if ( stream->total == 0 )
- {
- // return if there is no more data from fifo
- if ( !tud_midi_n_packet_read(itf, stream->buffer) ) return total_read;
+ if (stream->total == 0) {
+ if (!tud_midi_n_packet_read(itf, stream->buffer)) {
+ return total_read; // return if there is no more data from fifo
+ }
- uint8_t const code_index = stream->buffer[0] & 0x0f;
+ const uint8_t code_index = stream->buffer[0] & 0x0f;
// MIDI 1.0 Table 4-1: Code Index Number Classifications
- switch(code_index)
- {
+ switch (code_index) {
case MIDI_CIN_MISC:
case MIDI_CIN_CABLE_EVENT:
// These are reserved and unused, possibly issue somewhere, skip this packet
return 0;
- break;
case MIDI_CIN_SYSEX_END_1BYTE:
case MIDI_CIN_1BYTE_DATA:
stream->total = 1;
- break;
+ break;
case MIDI_CIN_SYSCOM_2BYTE :
case MIDI_CIN_SYSEX_END_2BYTE :
case MIDI_CIN_PROGRAM_CHANGE :
case MIDI_CIN_CHANNEL_PRESSURE :
stream->total = 2;
- break;
+ break;
default:
stream->total = 3;
- break;
+ break;
}
}
// Copy data up to bufsize
- uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize);
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize);
// Skip the header (1st byte) in the buffer
TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
@@ -189,8 +159,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
bufsize -= count;
// complete current event packet, reset stream
- if ( stream->total == stream->index )
- {
+ if (stream->total == stream->index) {
stream->index = 0;
stream->total = 0;
}
@@ -199,150 +168,206 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
return total_read;
}
-bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
-{
- midid_interface_t* midi = &_midid_itf[itf];
- TU_VERIFY(midi->ep_out);
+// Note: this function shares stream->buffer with tud_midi_n_stream_read().
+// Do not mix calls to both functions on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(p_cable_num != NULL && buffer != NULL && bufsize > 0, 0);
- const uint32_t num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
- _prep_out_transaction(itf);
- return (num_read == 4);
-}
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_read;
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
-//--------------------------------------------------------------------+
-// WRITE API
-//--------------------------------------------------------------------+
+ uint8_t *buf8 = (uint8_t *)buffer;
+ uint32_t total_read = 0;
-static uint32_t write_flush(uint8_t idx) {
- midid_interface_t* midi = &_midid_itf[idx];
+ // Initialize to invalid cable so callers can detect "no data" even when
+ // the return value is 0.
+ *p_cable_num = 0xff;
- if (!tu_fifo_count(&midi->tx_ff)) {
- return 0; // No data to send
+ // If there are leftover bytes from a previous partial read, return them first
+ if (stream->total > 0) {
+ *p_cable_num = (stream->buffer[0] >> 4) & 0x0f;
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
+
+ total_read += count;
+ stream->index += count;
+ buf8 += count;
+ bufsize -= count;
+
+ if (stream->total == stream->index) {
+ stream->index = 0;
+ stream->total = 0;
+ }
+
+ if (bufsize == 0) {
+ return total_read;
+ }
}
- const uint8_t rhport = 0;
+ while (bufsize > 0) {
+ // Peek at next packet header to get cable number without consuming
+ uint8_t one_byte;
+ if (!tu_edpt_stream_peek(ep_str, &one_byte)) {
+ break;
+ }
+
+ const uint8_t next_cable = (one_byte >> 4) & 0x0f;
+
+ // Stop if cable changed (covers both leftover-originated reads and
+ // freshly consumed packets — total_read > 0 in either case)
+ if (total_read > 0 && next_cable != *p_cable_num) {
+ break;
+ }
+ *p_cable_num = next_cable;
+
+ // Consume the packet
+ if (!tud_midi_n_packet_read(itf, stream->buffer)) {
+ break;
+ }
+
+ const uint8_t code_index = stream->buffer[0] & 0x0f;
+ uint8_t msg_bytes;
+
+ // MIDI 1.0 Table 4-1: Code Index Number Classifications
+ switch (code_index) {
+ case MIDI_CIN_MISC:
+ case MIDI_CIN_CABLE_EVENT:
+ // Reserved and unused, skip this packet
+ continue;
+
+ case MIDI_CIN_SYSEX_END_1BYTE:
+ case MIDI_CIN_1BYTE_DATA:
+ msg_bytes = 1;
+ break;
+
+ case MIDI_CIN_SYSCOM_2BYTE:
+ case MIDI_CIN_SYSEX_END_2BYTE:
+ case MIDI_CIN_PROGRAM_CHANGE:
+ case MIDI_CIN_CHANNEL_PRESSURE:
+ msg_bytes = 2;
+ break;
+
+ default:
+ msg_bytes = 3;
+ break;
+ }
- // skip if previous transfer not complete
- TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)msg_bytes, bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1, count));
- uint16_t count = tu_fifo_read_n(&midi->tx_ff, _midid_epbuf[idx].epin, CFG_TUD_MIDI_EP_BUFSIZE);
+ total_read += count;
+ buf8 += count;
+ bufsize -= count;
- if (count) {
- TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, _midid_epbuf[idx].epin, count), 0 );
- return count;
- }else {
- // Release endpoint since we don't make any transfer
- usbd_edpt_release(rhport, midi->ep_in);
- return 0;
+ if (count < msg_bytes) {
+ // Output buffer full, save remaining for next call
+ stream->total = msg_bytes;
+ stream->index = count;
+ }
}
+
+ return total_read;
}
-uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t* buffer, uint32_t bufsize)
-{
- midid_interface_t* midi = &_midid_itf[itf];
- TU_VERIFY(midi->ep_in, 0);
+bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+ return 4 == tu_edpt_stream_read(ep_str, packet, 4);
+}
- midi_driver_stream_t* stream = &midi->stream_write;
+uint32_t tud_midi_n_packet_read_n(uint8_t itf, uint8_t packets[], uint32_t max_packets) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+ const uint32_t num_read = tu_edpt_stream_read(ep_str, packets, 4u * max_packets);
+ return num_read >> 2u;
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_write;
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
uint32_t i = 0;
- while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
- {
+ while (i < bufsize) {
+ if (tu_edpt_stream_write_available(ep_str) < 4) {
+ break;
+ }
+
const uint8_t data = buffer[i];
i++;
- if ( stream->index == 0 )
- {
+ if (stream->index == 0) {
//------------- New event packet -------------//
const uint8_t msg = data >> 4;
- stream->index = 2;
+ stream->index = 2;
stream->buffer[1] = data;
// Check to see if we're still in a SysEx transmit.
- if ( ((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START )
- {
- if ( data == MIDI_STATUS_SYSEX_END )
- {
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
- stream->total = 2;
- }
- else
- {
+ if (((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START) {
+ if (data == MIDI_STATUS_SYSEX_END) {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
+ stream->total = 2;
+ } else {
stream->total = 4;
}
- }
- else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
- {
+ } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
// Channel Voice Messages
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
- stream->total = 4;
- }
- else if ( msg == 0xC || msg == 0xD)
- {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | msg);
+ stream->total = 4;
+ } else if (msg == 0xC || msg == 0xD) {
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
- stream->total = 3;
- }
- else if ( msg == 0xf )
- {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | msg);
+ stream->total = 3;
+ } else if (msg == 0xf) {
// System message
- if ( data == MIDI_STATUS_SYSEX_START )
- {
+ if (data == MIDI_STATUS_SYSEX_START) {
stream->buffer[0] = MIDI_CIN_SYSEX_START;
- stream->total = 4;
- }
- else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT )
- {
+ stream->total = 4;
+ } else if (data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT) {
stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
- stream->total = 3;
- }
- else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER )
- {
+ stream->total = 3;
+ } else if (data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER) {
stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
- stream->total = 4;
- }
- else
- {
+ stream->total = 4;
+ } else {
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
- stream->total = 2;
+ stream->total = 2;
}
stream->buffer[0] |= (uint8_t)(cable_num << 4);
- }
- else
- {
+ } else {
// Pack individual bytes if we don't support packing them into words.
- stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
+ stream->buffer[0] = (uint8_t)(cable_num << 4 | 0xf);
stream->buffer[2] = 0;
stream->buffer[3] = 0;
- stream->index = 2;
- stream->total = 2;
+ stream->total = 2; // index already set to 2
}
- }
- else
- {
+ } else {
//------------- On-going (buffering) packet -------------//
-
TU_ASSERT(stream->index < 4, i);
stream->buffer[stream->index] = data;
stream->index++;
// See if this byte ends a SysEx.
- if ( (stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
- {
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
- stream->total = stream->index;
+ if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
+ stream->total = stream->index;
}
}
// Send out packet
- if ( stream->index == stream->total )
- {
+ if (stream->index == stream->total) {
// zeroes unused bytes
for (uint8_t idx = stream->total; idx < 4; idx++) {
stream->buffer[idx] = 0;
}
- const uint16_t count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+ const uint32_t count = tu_edpt_stream_write(ep_str, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = stream->total = 0;
@@ -352,98 +377,107 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t*
}
}
- write_flush(itf);
+ (void)tu_edpt_stream_write_xfer(ep_str);
return i;
}
bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
- midid_interface_t* midi = &_midid_itf[itf];
- TU_VERIFY(midi->ep_in);
-
- if (tu_fifo_remaining(&midi->tx_ff) < 4) {
- return false;
- }
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str));
- tu_fifo_write_n(&midi->tx_ff, packet, 4);
- write_flush(itf);
+ TU_VERIFY(tu_edpt_stream_write_available(ep_str) >= 4);
+ TU_VERIFY(tu_edpt_stream_write(ep_str, packet, 4) > 0);
+ (void)tu_edpt_stream_write_xfer(ep_str);
return true;
}
+uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_t n_packets) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
+
+ uint32_t n_bytes = tu_edpt_stream_write_available(ep_str);
+ n_bytes = tu_min32(tu_align4(n_bytes), n_packets << 2u);
+
+ const uint32_t n_write = tu_edpt_stream_write(ep_str, packets, n_bytes);
+ (void)tu_edpt_stream_write_xfer(ep_str);
+
+ return n_write >> 2u;
+}
+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void midid_init(void) {
tu_memclr(_midid_itf, sizeof(_midid_itf));
-
for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
- midid_interface_t* midi = &_midid_itf[i];
+ midid_interface_t *p_midi = &_midid_itf[i];
- // config fifo
- tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, false); // true, true
- tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ midid_epbuf_t *p_epbuf = &_midid_epbuf[i];
+ uint8_t *epout_buf = p_epbuf->epout;
+ uint8_t *epin_buf = p_epbuf->epin;
+ #endif
- #if CFG_FIFO_MUTEX
- osal_mutex_t mutex_rd = osal_mutex_create(&midi->rx_ff_mutex);
- osal_mutex_t mutex_wr = osal_mutex_create(&midi->tx_ff_mutex);
- TU_ASSERT(mutex_wr != NULL && mutex_wr != NULL, );
+ tu_edpt_stream_init(&p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf,
+ CFG_TUD_MIDI_RX_BUFSIZE, epout_buf);
- tu_fifo_config_mutex(&midi->rx_ff, NULL, mutex_rd);
- tu_fifo_config_mutex(&midi->tx_ff, mutex_wr, NULL);
- #endif
+ tu_edpt_stream_init(&p_midi->ep_stream.tx, false, true, false, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE,
+ epin_buf);
}
}
bool midid_deinit(void) {
- #if CFG_FIFO_MUTEX
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++) {
- midid_interface_t* midi = &_midid_itf[i];
- osal_mutex_t mutex_rd = midi->rx_ff.mutex_rd;
- osal_mutex_t mutex_wr = midi->tx_ff.mutex_wr;
-
- if (mutex_rd) {
- osal_mutex_delete(mutex_rd);
- tu_fifo_config_mutex(&midi->rx_ff, NULL, NULL);
- }
-
- if (mutex_wr) {
- osal_mutex_delete(mutex_wr);
- tu_fifo_config_mutex(&midi->tx_ff, NULL, NULL);
- }
+ for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
+ midid_interface_t *p_midi = &_midid_itf[i];
+ tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
+ tu_edpt_stream_deinit(&p_midi->ep_stream.tx);
}
- #endif
-
return true;
}
-void midid_reset(uint8_t rhport)
-{
- (void) rhport;
+void midid_reset(uint8_t rhport) {
+ (void)rhport;
+ for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
+ midid_interface_t *p_midi = &_midid_itf[i];
+ tu_memclr(p_midi, ITF_MEM_RESET_SIZE);
+
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ tu_edpt_stream_close(&p_midi->ep_stream.rx);
+
+ tu_edpt_stream_clear(&p_midi->ep_stream.tx);
+ tu_edpt_stream_close(&p_midi->ep_stream.tx);
+ }
+}
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- midid_interface_t* midi = &_midid_itf[i];
- tu_memclr(midi, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&midi->rx_ff);
- tu_fifo_clear(&midi->tx_ff);
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_midi_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_MIDI; idx++) {
+ const midid_interface_t *p_midi = &_midid_itf[idx];
+ if (ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr) {
+ return idx;
+ }
}
+ return TUSB_INDEX_INVALID_8;
}
-uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
- uint16_t drv_len = 0;
- uint8_t const * p_desc = (uint8_t const *)desc_itf;
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
+ const uint8_t *p_desc = (const uint8_t *)desc_itf;
+ const uint8_t *desc_end = p_desc + max_len;
// 1st Interface is Audio Control v1 (optional)
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
- drv_len = tu_desc_len(desc_itf);
p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
- while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
+ p_desc = tu_desc_next(p_desc);
}
}
@@ -451,59 +485,48 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint1
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
- AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
- AUDIO_FUNC_PROTOCOL_CODE_UNDEF == 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;
- uint8_t idx;
- for(idx=0; idx<CFG_TUD_MIDI; idx++) {
- if ( _midid_itf[idx].ep_in == 0 && _midid_itf[idx].ep_out == 0 ) {
- p_midi = &_midid_itf[idx];
- break;
- }
- }
- TU_ASSERT(p_midi);
+ uint8_t idx = find_midi_itf(0); // find unused interface
+ TU_ASSERT(idx < CFG_TUD_MIDI, 0);
+ midid_interface_t *p_midi = &_midid_itf[idx];
+ p_midi->rhport = rhport;
p_midi->itf_num = desc_midi->bInterfaceNumber;
(void) p_midi->itf_num;
- // next descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
// Find and open endpoint descriptors
- uint8_t found_endpoints = 0;
- while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
- {
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- TU_ASSERT(usbd_edpt_open(rhport, (const tusb_desc_endpoint_t*) p_desc), 0);
- uint8_t ep_addr = ((const tusb_desc_endpoint_t*) p_desc)->bEndpointAddress;
+ uint8_t found_ep = 0;
+ while ((found_ep < desc_midi->bNumEndpoints) && tu_desc_in_bounds(p_desc, desc_end)) {
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ const uint8_t ep_addr = ((const tusb_desc_endpoint_t *)p_desc)->bEndpointAddress;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
- {
- p_midi->ep_in = ep_addr;
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx;
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_MIDI_TX_EPSIZE);
+ tu_edpt_stream_clear(stream_tx);
} else {
- p_midi->ep_out = ep_addr;
+ tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep));
+ tu_edpt_stream_clear(stream_rx);
+ TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0); // prepare to receive data
}
- // Class Specific MIDI Stream endpoint descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
-
- found_endpoints += 1;
+ p_desc = tu_desc_next(p_desc); // skip CS Endpoint descriptor
+ found_ep++;
}
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- // Prepare for incoming data
- _prep_out_transaction(idx);
-
- return drv_len;
+ return (uint16_t)(p_desc - (const uint8_t *)desc_itf);
}
// Invoked when a control transfer occurred on an interface of this class
@@ -514,44 +537,32 @@ bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_req
return false; // driver doesn't support any request yet
}
-bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) result;
- (void) rhport;
-
- uint8_t idx;
- midid_interface_t* p_midi;
+bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void)rhport;
+ (void)result;
- // Identify which interface to use
- for (idx = 0; idx < CFG_TUD_MIDI; idx++) {
- p_midi = &_midid_itf[idx];
- if ((ep_addr == p_midi->ep_out) || (ep_addr == p_midi->ep_in)) {
- break;
- }
- }
+ uint8_t idx = find_midi_itf(ep_addr);
TU_ASSERT(idx < CFG_TUD_MIDI);
+ midid_interface_t *p_midi = &_midid_itf[idx];
- // receive new data
- if (ep_addr == p_midi->ep_out) {
- tu_fifo_write_n(&p_midi->rx_ff, _midid_epbuf[idx].epout, (uint16_t)xferred_bytes);
-
- // invoke receive callback if available
- tud_midi_rx_cb(idx);
+ tu_edpt_stream_t *ep_st_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_t *ep_st_tx = &p_midi->ep_stream.tx;
- // 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
- _prep_out_transaction(idx);
- } else if (ep_addr == p_midi->ep_in) {
- if (0 == write_flush(idx)) {
- // If there is no data left, a ZLP should be sent if
- // xferred_bytes is multiple of EP size and not zero
- if (!tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE))) {
- if (usbd_edpt_claim(rhport, p_midi->ep_in)) {
- usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
- }
- }
+ if (ep_addr == ep_st_rx->ep_addr) {
+ // Received new data: put into stream's fifo
+ if (result == XFER_RESULT_SUCCESS) {
+ tu_edpt_stream_read_xfer_complete(ep_st_rx, xferred_bytes);
+ tud_midi_rx_cb(idx); // invoke callback
}
+ tu_edpt_stream_read_xfer(ep_st_rx); // prepare for next data
+ } else if (ep_addr == ep_st_tx->ep_addr && result == XFER_RESULT_SUCCESS) {
+ // sent complete: try to send more if possible
+ if (0 == tu_edpt_stream_write_xfer(ep_st_tx)) {
+ // If there is no data left, a ZLP should be sent if needed
+ (void)tu_edpt_stream_write_zlp_if_needed(ep_st_tx, xferred_bytes);
+ }
+ } else {
+ return false;
}
return true;
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index c2c6e9859..57eabec1f 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MIDI_DEVICE_H_
-#define _TUSB_MIDI_DEVICE_H_
+#ifndef TUSB_MIDI_DEVICE_H_
+#define TUSB_MIDI_DEVICE_H_
#include "class/audio/audio.h"
#include "midi.h"
@@ -34,23 +34,30 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
-#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
- #warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
- #define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
+#ifndef CFG_TUD_MIDI_RX_EPSIZE
+ #ifdef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_RX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
+ #else
+ #define CFG_TUD_MIDI_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
-#ifndef CFG_TUD_MIDI_EP_BUFSIZE
- #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#ifndef CFG_TUD_MIDI_TX_EPSIZE
+ #ifdef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_TX_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
+ #else
+ #define CFG_TUD_MIDI_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-/** \addtogroup MIDI_Serial Serial
- * @{
- * \defgroup MIDI_Serial_Device Device
- * @{ */
+//--------------------------------------------------------------------+
+// Application Callback API (optional)
+//--------------------------------------------------------------------+
+void tud_midi_rx_cb(uint8_t itf);
//--------------------------------------------------------------------+
// Application API (Multiple Interfaces)
@@ -58,117 +65,89 @@
//--------------------------------------------------------------------+
// Check if midi interface is mounted
-bool tud_midi_n_mounted (uint8_t itf);
+bool tud_midi_n_mounted(uint8_t itf);
// Get the number of bytes available for reading
-uint32_t tud_midi_n_available (uint8_t itf, uint8_t cable_num);
-
-// Read byte stream (legacy)
-uint32_t tud_midi_n_stream_read (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize);
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num);
-// Write byte Stream (legacy)
-uint32_t tud_midi_n_stream_write (uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
+// Read byte stream (legacy)
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize);
-// Read event packet (4 bytes)
-bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4]);
+// Read byte stream with cable demultiplexing: returns the cable number of the
+// data that was read. Reads from a single cable per call; stops when the next
+// packet belongs to a different cable so the caller can dispatch per-cable.
+// Note: shares internal state with tud_midi_n_stream_read(); do not mix both
+// on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize);
-// Write event packet (4 bytes)
-bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]);
-
-//--------------------------------------------------------------------+
-// Application API (Single Interface)
-//--------------------------------------------------------------------+
-static inline bool tud_midi_mounted (void);
-static inline uint32_t tud_midi_available (void);
+// Write byte Stream (legacy)
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize);
-static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize);
-static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
+// Read an event 4-byte packet
+bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]);
-static inline bool tud_midi_packet_read (uint8_t packet[4]);
-static inline bool tud_midi_packet_write (uint8_t const packet[4]);
+// Read multiple event packets, return number of read packets
+uint32_t tud_midi_n_packet_read_n(uint8_t itf, uint8_t packets[], uint32_t max_packets);
-//------------- Deprecated API name -------------//
-// TODO remove after 0.10.0 release
+// Write an event 4-byte packet
+bool tud_midi_n_packet_write(uint8_t itf, const uint8_t packet[4]);
-TU_ATTR_DEPRECATED("tud_midi_read() is renamed to tud_midi_stream_read()")
-static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize)
-{
- return tud_midi_stream_read(buffer, bufsize);
-}
-
-TU_ATTR_DEPRECATED("tud_midi_write() is renamed to tud_midi_stream_write()")
-static inline uint32_t tud_midi_write(uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
-{
- return tud_midi_stream_write(cable_num, buffer, bufsize);
-}
-
-
-TU_ATTR_DEPRECATED("tud_midi_send() is renamed to tud_midi_packet_write()")
-static inline bool tud_midi_send(uint8_t packet[4])
-{
- return tud_midi_packet_write(packet);
-}
-
-TU_ATTR_DEPRECATED("tud_midi_receive() is renamed to tud_midi_packet_read()")
-static inline bool tud_midi_receive(uint8_t packet[4])
-{
- return tud_midi_packet_read(packet);
-}
+// Write multiple event packets, return number of written packets
+uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_t n_packets);
//--------------------------------------------------------------------+
-// Application Callback API (optional)
-//--------------------------------------------------------------------+
-void tud_midi_rx_cb(uint8_t itf);
-
-//--------------------------------------------------------------------+
-// Inline Functions
+// Application API (Single Interface)
//--------------------------------------------------------------------+
-
-static inline bool tud_midi_mounted (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_mounted(void) {
return tud_midi_n_mounted(0);
}
-static inline uint32_t tud_midi_available (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_available(void) {
return tud_midi_n_available(0, 0);
}
-static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_read(void *buffer, uint32_t bufsize) {
return tud_midi_n_stream_read(0, 0, buffer, bufsize);
}
-static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi_demux_stream_read(uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ return tud_midi_n_demux_stream_read(0, p_cable_num, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi_stream_write(uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
}
-static inline bool tud_midi_packet_read (uint8_t packet[4])
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_packet_read(uint8_t packet[4]) {
return tud_midi_n_packet_read(0, packet);
}
-static inline bool tud_midi_packet_write (uint8_t const packet[4])
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_packet_read_n(uint8_t packets[], uint32_t max_packets) {
+ return tud_midi_n_packet_read_n(0, packets, max_packets);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_packet_write(const uint8_t packet[4]) {
return tud_midi_n_packet_write(0, packet);
}
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_packet_write_n(const uint8_t packets[], uint32_t n_packets) {
+ return tud_midi_n_packet_write_n(0, packets, n_packets);
+}
+
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void midid_init (void);
-bool midid_deinit (void);
-void midid_reset (uint8_t rhport);
-uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+void midid_init(void);
+bool midid_deinit(void);
+void midid_reset(uint8_t rhport);
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request);
+bool midid_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_MIDI_DEVICE_H_ */
-
-/** @} */
-/** @} */
+#endif
diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c
index e6ace316c..a53546404 100644
--- a/src/class/midi/midi_host.c
+++ b/src/class/midi/midi_host.c
@@ -57,10 +57,7 @@ typedef struct {
uint8_t daddr;
uint8_t bInterfaceNumber; // interface number of MIDI streaming
uint8_t iInterface;
- uint8_t itf_count; // number of interface including Audio Control + MIDI streaming
-
- uint8_t ep_in; // IN endpoint address
- uint8_t ep_out; // OUT endpoint address
+ uint8_t itf_count; // number of interfaces including Audio Control + MIDI streaming
uint8_t rx_cable_count; // IN endpoint CS descriptor bNumEmbMIDIJack value
uint8_t tx_cable_count; // OUT endpoint CS descriptor bNumEmbMIDIJack value
@@ -86,8 +83,8 @@ typedef struct {
}midih_interface_t;
typedef struct {
- TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS);
- TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS);
+ TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MAX);
+ TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MAX);
} midih_epbuf_t;
static midih_interface_t _midi_host[CFG_TUH_MIDI];
@@ -124,9 +121,9 @@ bool midih_init(void) {
for (int inst = 0; inst < CFG_TUH_MIDI; inst++) {
midih_interface_t *p_midi_host = &_midi_host[inst];
tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false,
- p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS);
+ p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf[inst].rx);
tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false,
- p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS);
+ p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf[inst].tx);
}
return true;
}
@@ -147,8 +144,6 @@ void midih_close(uint8_t daddr) {
TU_LOG_DRV(" MIDI close addr = %u index = %u\r\n", daddr, idx);
tuh_midi_umount_cb(idx);
- p_midi->ep_in = 0;
- p_midi->ep_out = 0;
p_midi->bInterfaceNumber = 0;
p_midi->rx_cable_count = 0;
p_midi->tx_cable_count = 0;
@@ -169,23 +164,25 @@ bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
+ tu_edpt_stream_t *ep_str_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_t *ep_str_tx = &p_midi->ep_stream.tx;
- if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
+ if (ep_addr == ep_str_rx->ep_addr) {
// receive new data, put it into FIFO and invoke callback if available
// Note: some devices send back all zero packets even if there is no data ready
- if (xferred_bytes && !tu_mem_is_zero(p_midi->ep_stream.rx.ep_buf, xferred_bytes)) {
- tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
+ if (xferred_bytes && !tu_mem_is_zero(ep_str_rx->ep_buf, xferred_bytes)) {
+ tu_edpt_stream_read_xfer_complete(ep_str_rx, xferred_bytes);
tuh_midi_rx_cb(idx, xferred_bytes);
}
- tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for next transfer
- } else if (ep_addr == p_midi->ep_stream.tx.ep_addr) {
+ tu_edpt_stream_read_xfer(ep_str_rx); // prepare for next transfer
+ } else if (ep_addr == ep_str_tx->ep_addr) {
tuh_midi_tx_cb(idx, xferred_bytes);
- if (0 == tu_edpt_stream_write_xfer(dev_addr, &p_midi->ep_stream.tx)) {
+ if (0 == tu_edpt_stream_write_xfer(ep_str_tx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
- tu_edpt_stream_write_zlp_if_needed(dev_addr, &p_midi->ep_stream.tx, xferred_bytes);
+ tu_edpt_stream_write_zlp_if_needed(ep_str_tx, xferred_bytes);
}
}
@@ -195,15 +192,16 @@ bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
-bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
+uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
- const uint8_t *p_end = ((const uint8_t *) desc_itf) + max_len;
- const uint8_t *p_desc = (const uint8_t *) desc_itf;
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
+ const uint8_t *desc_start = (const uint8_t *)desc_itf;
+ const uint8_t *p_desc = desc_start;
+ const uint8_t *desc_end = desc_start + max_len;
const uint8_t idx = find_new_midi_index();
- TU_VERIFY(idx < CFG_TUH_MIDI);
+ TU_VERIFY(idx < CFG_TUH_MIDI, 0);
midih_interface_t *p_midi = &_midi_host[idx];
p_midi->itf_count = 0;
@@ -220,29 +218,30 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
// driver after parsing the audio control interface and then resume parsing
// the streaming audio interface.
if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
- TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(audio_desc_cs_ac_interface_t));
-
+ TU_VERIFY(max_len > 2 * sizeof(tusb_desc_interface_t) + sizeof(midi10_desc_cs_ac_interface_t), 0);
p_desc = tu_desc_next(p_desc);
TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
- tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_HEADER);
+ tu_desc_subtype(p_desc) == AUDIO10_CS_AC_INTERFACE_HEADER,
+ 0);
desc_cb.desc_audio_control = desc_itf;
p_desc = tu_desc_next(p_desc);
desc_itf = (const tusb_desc_interface_t *)p_desc;
p_midi->itf_count = 1;
// skip non-interface and non-midi streaming descriptors
- while (tu_desc_in_bounds(p_desc, p_end) &&
- (desc_itf->bDescriptorType != TUSB_DESC_INTERFACE || (desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO && desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
+ while (tu_desc_in_bounds(p_desc, desc_end) && (desc_itf->bDescriptorType != TUSB_DESC_INTERFACE ||
+ (desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO &&
+ desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
if (desc_itf->bDescriptorType == TUSB_DESC_INTERFACE && desc_itf->bAlternateSetting == 0) {
p_midi->itf_count++;
}
p_desc = tu_desc_next(p_desc);
- desc_itf = (tusb_desc_interface_t const *)p_desc;
+ desc_itf = (const tusb_desc_interface_t *)p_desc;
}
- TU_VERIFY(p_desc < p_end); // TODO: If MIDI interface comes after Audio Streaming, then max_len did not include the MIDI interface descriptor
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
+ TU_VERIFY(p_desc < desc_end, 0);
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass, 0);
}
- TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass);
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, 0);
TU_LOG_DRV("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
@@ -250,10 +249,12 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
p_midi->itf_count++;
desc_cb.desc_midi = desc_itf;
- p_desc = tu_desc_next(p_desc); // next to CS Header
-
bool found_new_interface = false;
- while (tu_desc_in_bounds(p_desc, p_end) && !found_new_interface) {
+ do {
+ p_desc = tu_desc_next(p_desc);
+ if (!tu_desc_in_bounds(p_desc, desc_end)) {
+ break;
+ }
switch (tu_desc_type(p_desc)) {
case TUSB_DESC_INTERFACE:
found_new_interface = true;
@@ -290,39 +291,39 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
case TUSB_DESC_ENDPOINT: {
const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
+
p_desc = tu_desc_next(p_desc); // next to CS endpoint
- TU_VERIFY(p_desc < p_end && tu_desc_next(p_desc) <= p_end);
+ TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0);
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
+ tu_edpt_stream_t *ep_stream;
if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
- p_midi->ep_out = p_ep->bEndpointAddress;
p_midi->tx_cable_count = p_csep->bNumEmbMIDIJack;
desc_cb.desc_epout = p_ep;
-
- TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
- tu_edpt_stream_open(&p_midi->ep_stream.tx, p_ep);
+ ep_stream = &p_midi->ep_stream.tx;
} else {
- p_midi->ep_in = p_ep->bEndpointAddress;
p_midi->rx_cable_count = p_csep->bNumEmbMIDIJack;
- desc_cb.desc_epin = p_ep;
-
- TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
- tu_edpt_stream_open(&p_midi->ep_stream.rx, p_ep);
+ desc_cb.desc_epin = p_ep;
+ ep_stream = &p_midi->ep_stream.rx;
}
+ TU_ASSERT(tuh_edpt_open(dev_addr, p_ep), 0);
+ tu_edpt_stream_open(ep_stream, dev_addr, p_ep, tu_edpt_packet_size(p_ep));
+ tu_edpt_stream_clear(ep_stream);
+
break;
}
default: break; // skip unknown descriptor
}
- p_desc = tu_desc_next(p_desc);
- }
- desc_cb.desc_midi_total_len = (uint16_t) ((uintptr_t)p_desc - (uintptr_t) desc_itf);
+ } while (!found_new_interface);
+
+ desc_cb.desc_midi_total_len = (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_start);
p_midi->daddr = dev_addr;
tuh_midi_descriptor_cb(idx, &desc_cb);
- return true;
+ return desc_cb.desc_midi_total_len;
}
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
@@ -333,13 +334,13 @@ bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
const tuh_midi_mount_cb_t mount_cb_data = {
.daddr = dev_addr,
- .bInterfaceNumber = itf_num,
+ .bInterfaceNumber = p_midi->bInterfaceNumber,
.rx_cable_count = p_midi->rx_cable_count,
.tx_cable_count = p_midi->tx_cable_count,
};
tuh_midi_mount_cb(idx, &mount_cb_data);
- tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for incoming data
+ tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx); // prepare for incoming data
// No special config things to do for MIDI
usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
@@ -379,8 +380,14 @@ bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
desc->bDescriptorType = TUSB_DESC_INTERFACE;
desc->bInterfaceNumber = p_midi->bInterfaceNumber;
- desc->bAlternateSetting = 0;
- desc->bNumEndpoints = (uint8_t)((p_midi->ep_in != 0 ? 1:0) + (p_midi->ep_out != 0 ? 1:0));
+ desc->bAlternateSetting = 0;
+ desc->bNumEndpoints = 0;
+ if (tu_edpt_stream_is_opened(&p_midi->ep_stream.tx)) {
+ desc->bNumEndpoints++;
+ }
+ if (tu_edpt_stream_is_opened(&p_midi->ep_stream.rx)) {
+ desc->bNumEndpoints++;
+ }
desc->bInterfaceClass = TUSB_CLASS_AUDIO;
desc->bInterfaceSubClass = AUDIO_SUBCLASS_MIDI_STREAMING;
desc->bInterfaceProtocol = 0;
@@ -412,7 +419,7 @@ uint32_t tuh_midi_read_available(uint8_t idx) {
uint32_t tuh_midi_write_flush(uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
- return tu_edpt_stream_write_xfer(p_midi->daddr, &p_midi->ep_stream.tx);
+ return tu_edpt_stream_write_xfer(&p_midi->ep_stream.tx);
}
//--------------------------------------------------------------------+
@@ -425,7 +432,7 @@ uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize)
uint32_t count4 = tu_min32(bufsize, tu_edpt_stream_read_available(&p_midi->ep_stream.rx));
count4 = tu_align4(count4); // round down to multiple of 4
TU_VERIFY(count4 > 0, 0);
- return tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, buffer, count4);
+ return tu_edpt_stream_read(&p_midi->ep_stream.rx, buffer, count4);
}
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
@@ -434,7 +441,7 @@ uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bu
const uint32_t bufsize4 = tu_align4(bufsize);
TU_VERIFY(bufsize4 > 0, 0);
- return tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, buffer, bufsize4);
+ return tu_edpt_stream_write(&p_midi->ep_stream.tx, buffer, bufsize4);
}
//--------------------------------------------------------------------+
@@ -448,17 +455,17 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
midi_driver_stream_t *stream = &p_midi->stream_write;
uint32_t byte_count = 0;
- while ((byte_count < bufsize) && (tu_edpt_stream_write_available(p_midi->daddr, &p_midi->ep_stream.tx) >= 4)) {
- uint8_t const data = buffer[byte_count];
+ while ((byte_count < bufsize) && (tu_edpt_stream_write_available(&p_midi->ep_stream.tx) >= 4)) {
+ const uint8_t data = buffer[byte_count];
byte_count++;
if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
// real-time messages need to be sent right away
midi_driver_stream_t streamrt;
- streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ streamrt.buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
streamrt.buffer[1] = data;
streamrt.index = 2;
streamrt.total = 2;
- uint32_t const count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, streamrt.buffer, 4);
+ const uint32_t count = tu_edpt_stream_write(&p_midi->ep_stream.tx, streamrt.buffer, 4);
TU_ASSERT(count == 4, byte_count); // Check FIFO overflown, since we already check fifo remaining. It is probably race condition
} else if (stream->index == 0) {
//------------- New event packet -------------//
@@ -469,9 +476,9 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
stream->buffer[1] = data;
// Check to see if we're still in a SysEx transmit.
- if (stream->buffer[0] == MIDI_CIN_SYSEX_START) {
+ if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START) {
if (data == MIDI_STATUS_SYSEX_END) {
- stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
stream->total = 2;
} else {
stream->total = 4;
@@ -499,6 +506,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
stream->total = 2;
}
+ stream->buffer[0] |= (uint8_t)(cable_num << 4);
} else {
// Pack individual bytes if we don't support packing them into words.
stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
@@ -513,8 +521,8 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
stream->buffer[stream->index] = data;
stream->index++;
// See if this byte ends a SysEx.
- if (stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
- stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1);
+ if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
stream->total = stream->index;
}
}
@@ -527,7 +535,7 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *bu
}
TU_LOG3_MEM(stream->buffer, 4, 2);
- const uint32_t count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, stream->buffer, 4);
+ const uint32_t count = tu_edpt_stream_write(&p_midi->ep_stream.tx, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = 0;
@@ -549,7 +557,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
return 0;
}
*p_cable_num = (one_byte >> 4) & 0xf;
- uint32_t nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
+ uint32_t nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
static uint16_t cable_sysex_in_progress;// bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END
while (nread == 4 && bytes_buffered < bufsize) {
*p_cable_num = (p_midi->stream_read.buffer[0] >> 4) & 0x0f;
@@ -577,7 +585,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
}
else {
// bad packet discard
- nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
+ nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
continue;
}
} else if (status < MIDI_STATUS_SYSEX_START) {
@@ -624,7 +632,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
}
else {
// bad packet discard
- nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
+ nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
continue;
}
@@ -637,7 +645,7 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
uint8_t new_cable = (one_byte >> 4) & 0xf;
if (new_cable == *p_cable_num) {
// still on the same cable. Continue reading the stream
- nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
+ nread = tu_edpt_stream_read(&p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
}
}
}
diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h
index 06554a03d..8fdfd8966 100644
--- a/src/class/midi/midi_host.h
+++ b/src/class/midi/midi_host.h
@@ -31,45 +31,45 @@
#include "midi.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
-#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
-#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
-#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
+ #define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MAX
#endif
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
// Disable this can save driver footprint.
#ifndef CFG_TUH_MIDI_STREAM_API
-#define CFG_TUH_MIDI_STREAM_API 1
+ #define CFG_TUH_MIDI_STREAM_API 1
#endif
//--------------------------------------------------------------------+
// Application Types
//--------------------------------------------------------------------+
typedef struct {
- const tusb_desc_interface_t* desc_audio_control;
- const tusb_desc_interface_t* desc_midi; // start of whole midi interface descriptor
- uint16_t desc_midi_total_len;
+ const tusb_desc_interface_t *desc_audio_control;
+ const tusb_desc_interface_t *desc_midi; // start of whole midi interface descriptor
+ uint16_t desc_midi_total_len;
- const uint8_t* desc_header;
- const uint8_t* desc_element;
- const tusb_desc_endpoint_t* desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
- const tusb_desc_endpoint_t* desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
+ const uint8_t *desc_header;
+ const uint8_t *desc_element;
+ const tusb_desc_endpoint_t *desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
+ const tusb_desc_endpoint_t *desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
- uint8_t jack_num;
- const uint8_t* desc_jack[32]; // list of jack descriptors (embedded + external)
+ uint8_t jack_num;
+ const uint8_t *desc_jack[32]; // list of jack descriptors (embedded + external)
} tuh_midi_descriptor_cb_t;
typedef struct {
@@ -92,7 +92,7 @@ uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get Interface information
// return true if index is correct and interface is currently mounted
-bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
+bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t *info);
// return the number of virtual midi cables on the device's IN endpoint
uint8_t tuh_midi_get_rx_cable_count(uint8_t idx);
@@ -115,24 +115,22 @@ uint32_t tuh_midi_write_flush(uint8_t idx);
// Read all available MIDI packets from the connected device
// Return number of bytes read (always multiple of 4)
-uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize);
+uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t *buffer, uint32_t bufsize);
// Read a raw MIDI packet from the connected device
// Return true if a packet was returned
-TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_midi_packet_read (uint8_t idx, uint8_t packet[4]) {
- return 4 == tuh_midi_packet_read_n(idx, packet, 4);
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_midi_packet_read(uint8_t idx, uint8_t packet[4]) {
+ return 4 == tuh_midi_packet_read_n(idx, packet, 4);
}
// Write all 4-byte packets, data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
-uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize);
+uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
// Write a 4-bytes packet to the device.
// Returns true if the packet was successfully queued.
-TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
- return 4 == tuh_midi_packet_write_n(idx, packet, 4);
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_midi_packet_write(uint8_t idx, const uint8_t packet[4]) {
+ return 4 == tuh_midi_packet_write_n(idx, packet, 4);
}
//--------------------------------------------------------------------+
@@ -143,7 +141,7 @@ bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
// Queue a message to the device using stream API. data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
// Returns number of bytes was successfully queued.
-uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_buffer, uint32_t bufsize);
+uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, const uint8_t *p_buffer, uint32_t bufsize);
// Get the MIDI stream from the device. Set the value pointed
// to by p_cable_num to the MIDI cable number intended to receive it.
@@ -152,6 +150,13 @@ uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_
// Note that this function ignores the CIN field of the MIDI packet
// because a number of commercial devices out there do not encode
// it properly.
+//
+// NOTE: this function terminates when it encounters an event whose cable
+// number differs from the one being returned. Applications should invoke
+// it in a loop until it returns 0 (or until tuh_midi_read_available()
+// returns 0) to guarantee the stream FIFO is fully drained per callback.
+// Leaving bytes in the FIFO across callbacks can prevent subsequent bulk
+// IN transfers from landing.
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize);
#endif
@@ -162,10 +167,10 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
// Invoked when MIDI interface is detected in enumeration. Application can copy/parse descriptor if needed.
// Note: may be fired before tuh_midi_mount_cb(), therefore midi interface is not mounted/ready.
-void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data);
+void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t *desc_cb_data);
// Invoked when device with MIDI interface is mounted.
-void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data);
+void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t *mount_cb_data);
// Invoked when device with MIDI interface is un-mounted
void tuh_midi_umount_cb(uint8_t idx);
@@ -179,12 +184,12 @@ void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-bool midih_init (void);
-bool midih_deinit (void);
-bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
-bool midih_set_config (uint8_t dev_addr, uint8_t itf_num);
-bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-void midih_close (uint8_t daddr);
+bool midih_init(void);
+bool midih_deinit(void);
+uint16_t midih_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
+bool midih_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void midih_close(uint8_t daddr);
#ifdef __cplusplus
}
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index b2b44eac4..3b5d4a855 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_H_
-#define _TUSB_MSC_H_
+#ifndef TUSB_MSC_H_
+#define TUSB_MSC_H_
#include "common/tusb_common.h"
@@ -398,4 +398,4 @@ TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
}
#endif
-#endif /* _TUSB_MSC_H_ */
+#endif /* TUSB_MSC_H_ */
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index b0eafd5da..3766e3a25 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -83,7 +83,7 @@ typedef struct {
uint8_t add_sense_code;
uint8_t add_sense_qualifier;
- uint8_t pending_io; // pending async IO
+ bool pending_io; // pending async IO
}mscd_interface_t;
static mscd_interface_t _mscd_itf;
@@ -92,6 +92,8 @@ CFG_TUD_MEM_SECTION static struct {
TUD_EPBUF_DEF(buf, CFG_TUD_MSC_EP_BUFSIZE);
} _mscd_epbuf;
+TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE >= 64, "CFG_TUD_MSC_EP_BUFSIZE must be at least 64");
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
@@ -107,19 +109,19 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) {
return tu_bit_test(dir, 7);
}
-static inline bool send_csw(mscd_interface_t* p_msc) {
+TU_ATTR_ALWAYS_INLINE static inline bool send_csw(mscd_interface_t* p_msc) {
// Data residue is always = host expect - actual transferred
uint8_t rhport = p_msc->rhport;
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
p_msc->stage = MSC_STAGE_STATUS_SENT;
- memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t));
- return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t));
+ memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t)); //-V1086
+ return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t), false);
}
-static inline bool prepare_cbw(mscd_interface_t* p_msc) {
+TU_ATTR_ALWAYS_INLINE static inline bool prepare_cbw(mscd_interface_t* p_msc) {
uint8_t rhport = p_msc->rhport;
p_msc->stage = MSC_STAGE_CMD;
- return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t));
+ return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t), false);
}
static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) {
@@ -133,7 +135,7 @@ static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) {
// failed but sense key is not set: default to Illegal Request
if (p_msc->sense_key == 0) {
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ (void) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
}
// If there is data stage and not yet complete, stall it
@@ -146,18 +148,18 @@ static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) {
}
}
-static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
+TU_ATTR_ALWAYS_INLINE static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
// use offsetof to avoid pointer to the odd/unaligned address
const uint32_t lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
return tu_ntohl(lba); // lba is in Big Endian
}
-static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
return tu_ntohs(block_count);
}
-static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
// first extract block count in the command
uint16_t const block_count = rdwr10_get_blockcount(cbw);
if (block_count == 0) {
@@ -171,7 +173,7 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
uint16_t const block_count = rdwr10_get_blockcount(cbw);
if (cbw->total_bytes == 0) {
- if (block_count) {
+ if (block_count > 0) {
TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
} else {
@@ -190,6 +192,8 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
} else if (cbw->total_bytes / block_count == 0) {
TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
+ } else {
+ // nothing to do
}
}
@@ -309,7 +313,7 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u
TU_ATTR_ALWAYS_INLINE static inline void set_sense_medium_not_present(uint8_t lun) {
// default sense is NOT READY, MEDIUM NOT PRESENT
- tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
+ (void) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
}
static void proc_async_io_done(void *bytes_io) {
@@ -318,7 +322,7 @@ static void proc_async_io_done(void *bytes_io) {
const int32_t nbytes = (int32_t) (intptr_t) bytes_io;
const uint8_t cmd = p_msc->cbw.command[0];
- p_msc->pending_io = 0;
+ p_msc->pending_io = false;
switch (cmd) {
case SCSI_CMD_READ_10:
proc_read_io_data(p_msc, nbytes);
@@ -328,7 +332,7 @@ static void proc_async_io_done(void *bytes_io) {
proc_write_io_data(p_msc, (uint32_t) nbytes, nbytes);
break;
- default: break;
+ default: break; // nothing to do
}
// send status if stage is transitioned to STATUS
@@ -429,6 +433,8 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_ASSERT(prepare_cbw(p_msc));
}
}
+ } else {
+ // nothing to do
}
}
@@ -451,7 +457,7 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
uint8_t maxlun = tud_msc_get_maxlun_cb();
- TU_VERIFY(maxlun);
+ TU_VERIFY(maxlun != 0);
maxlun--; // MAX LUN is minus 1 by specs
tud_control_xfer(rhport, request, &maxlun, 1);
break;
@@ -510,7 +516,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
if (status != MSC_CSW_STATUS_PASSED) {
fail_scsi_op(p_msc, status);
- } else if (p_cbw->total_bytes) {
+ } else if (p_cbw->total_bytes > 0) {
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
proc_read10_cmd(p_msc);
} else {
@@ -531,7 +537,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
} else {
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
// but it is OK to just receive data then responded with failed status
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, (uint16_t) p_msc->total_len, false));
}
} else {
// First process if it is a built-in commands
@@ -547,7 +553,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
} else if (resplen == 0) {
- if (p_cbw->total_bytes) {
+ if (p_cbw->total_bytes > 0) {
// 6.7 The 13 Cases: case 4 (Hi > Dn)
// TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
@@ -563,7 +569,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
} else {
// cannot return more than host expect
p_msc->total_len = tu_min32((uint32_t)resplen, p_cbw->total_bytes);
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) p_msc->total_len, false));
}
}
}
@@ -640,14 +646,18 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
}
- TU_ASSERT(prepare_cbw(p_msc));
+ if (!usbd_edpt_stalled(rhport, p_msc->ep_out)) {
+ TU_ASSERT(prepare_cbw(p_msc));
+ } else {
+ p_msc->stage = MSC_STAGE_CMD;
+ }
} else {
// Any xfer ended here is considered unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
break;
- default: break;
+ default: break; // nothing to do
}
if (p_msc->stage == MSC_STAGE_STATUS) {
@@ -683,9 +693,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_START_STOP_UNIT:
+ case SCSI_CMD_START_STOP_UNIT: {
resplen = 0;
-
scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd;
if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) {
// Failed status response
@@ -697,10 +706,10 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
}
break;
+ }
- case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: {
resplen = 0;
-
scsi_prevent_allow_medium_removal_t const* prevent_allow = (scsi_prevent_allow_medium_removal_t const*)scsi_cmd;
if (!tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control)) {
// Failed status response
@@ -712,7 +721,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
}
break;
-
+ }
case SCSI_CMD_READ_CAPACITY_10: {
uint32_t block_count;
@@ -740,8 +749,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = sizeof(read_capa10);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) resplen));
}
+ break;
}
- break;
case SCSI_CMD_READ_FORMAT_CAPACITY: {
scsi_read_format_capacity_data_t read_fmt_capa = {
@@ -772,8 +781,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = sizeof(read_fmt_capa);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
}
+ break;
}
- break;
case SCSI_CMD_INQUIRY: {
scsi_inquiry_resp_t *inquiry_rsp = (scsi_inquiry_resp_t *) buffer;
@@ -789,8 +798,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
tud_msc_inquiry_cb(lun, inquiry_rsp->vendor_id, inquiry_rsp->product_id, inquiry_rsp->product_rev);
resplen = sizeof(scsi_inquiry_resp_t);
}
+ break;
}
- break;
case SCSI_CMD_MODE_SENSE_6: {
scsi_mode_sense6_resp_t mode_resp = {
@@ -807,8 +816,8 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = sizeof(mode_resp);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &mode_resp, (size_t) resplen));
+ break;
}
- break;
case SCSI_CMD_REQUEST_SENSE: {
scsi_sense_fixed_resp_t sense_rsp = {
@@ -828,9 +837,9 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
// Clear sense data after copy
- tud_msc_set_sense(lun, 0, 0, 0);
+ (void) tud_msc_set_sense(lun, 0, 0, 0);
+ break;
}
- break;
default: resplen = -1;
break;
@@ -842,6 +851,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
static void proc_read10_cmd(mscd_interface_t* p_msc) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
+ TU_VERIFY(block_sz != 0, );
// Adjust lba & offset with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
uint32_t const offset = p_msc->xferred_len % block_sz;
@@ -849,10 +859,10 @@ static void proc_read10_cmd(mscd_interface_t* p_msc) {
// remaining bytes capped at class buffer
int32_t nbytes = (int32_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
- p_msc->pending_io = 1;
+ p_msc->pending_io = true;
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes);
if (nbytes != TUD_MSC_RET_ASYNC) {
- p_msc->pending_io = 0;
+ p_msc->pending_io = false;
proc_read_io_data(p_msc, nbytes);
}
}
@@ -860,7 +870,7 @@ static void proc_read10_cmd(mscd_interface_t* p_msc) {
static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) {
const uint8_t rhport = p_msc->rhport;
if (nbytes > 0) {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes, false),);
} else {
// nbytes is status
switch (nbytes) {
@@ -876,19 +886,19 @@ static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) {
dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
break;
- default: break;
+ default: break; // nothing to do
}
}
}
static void proc_write10_cmd(mscd_interface_t* p_msc) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
- bool writable = tud_msc_is_writable_cb(p_cbw->lun);
+ const bool writable = tud_msc_is_writable_cb(p_cbw->lun);
if (!writable) {
// Not writable, complete this SCSI op with error
// Sense = Write protected
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
+ (void) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
return;
}
@@ -896,22 +906,23 @@ static void proc_write10_cmd(mscd_interface_t* p_msc) {
// remaining bytes capped at class buffer
uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
// Write10 callback will be called later when usb transfer complete
- TU_ASSERT(usbd_edpt_xfer(p_msc->rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
+ TU_ASSERT(usbd_edpt_xfer(p_msc->rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes, false),);
}
// process new data arrived from WRITE10
static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
+ TU_VERIFY(block_sz != 0, );
// Adjust lba & offset with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
uint32_t const offset = p_msc->xferred_len % block_sz;
- p_msc->pending_io = 1;
+ p_msc->pending_io = true;
int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
if (nbytes != TUD_MSC_RET_ASYNC) {
- p_msc->pending_io = 0;
+ p_msc->pending_io = false;
proc_write_io_data(p_msc, xferred_bytes, nbytes);
}
}
@@ -927,7 +938,7 @@ static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes,
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
break;
- default: break;
+ default: break; // nothing to do
}
} else {
if ((uint32_t)nbytes < xferred_bytes) {
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 7d898e988..21e24971b 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_DEVICE_H_
-#define _TUSB_MSC_DEVICE_H_
+#ifndef TUSB_MSC_DEVICE_H_
+#define TUSB_MSC_DEVICE_H_
#include "common/tusb_common.h"
#include "msc.h"
@@ -167,4 +167,4 @@ bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t ev
}
#endif
-#endif /* _TUSB_MSC_DEVICE_H_ */
+#endif /* TUSB_MSC_DEVICE_H_ */
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index eb69ae400..6a36c2820 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -123,7 +123,10 @@ bool tuh_msc_mounted(uint8_t dev_addr) {
bool tuh_msc_ready(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
- return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
+ TU_VERIFY(p_msc->mounted);
+ const bool epin_busy = usbh_edpt_busy(dev_addr, p_msc->ep_in);
+ const bool epout_busy = usbh_edpt_busy(dev_addr, p_msc->ep_out);
+ return !epin_busy && !epout_busy;
}
//--------------------------------------------------------------------+
@@ -152,7 +155,7 @@ bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
p_msc->stage = MSC_STAGE_CMD;
if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &epbuf->cbw, sizeof(msc_cbw_t))) {
- usbh_edpt_release(daddr, p_msc->ep_out);
+ (void) usbh_edpt_release(daddr, p_msc->ep_out);
return false;
}
@@ -191,7 +194,7 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
.cmd_code = SCSI_CMD_INQUIRY,
.alloc_length = sizeof(scsi_inquiry_resp_t)
};
- memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
@@ -225,7 +228,7 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
.cmd_code = SCSI_CMD_REQUEST_SENSE,
.alloc_length = 18
};
- memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
@@ -247,7 +250,7 @@ bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, u
.lba = tu_htonl(lba),
.block_count = tu_htons(block_count)
};
- memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_read10, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
}
@@ -269,7 +272,7 @@ bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t
.lba = tu_htonl(lba),
.block_count = tu_htons(block_count)
};
- memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_write10, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
}
@@ -338,8 +341,7 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
break;
}
-
- TU_ATTR_FALLTHROUGH; // fallthrough to status stage
+ TU_ATTR_FALLTHROUGH; // fallthrough to data stage
case MSC_STAGE_DATA:
// Status stage
@@ -350,20 +352,19 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
case MSC_STAGE_STATUS:
// SCSI op is complete
p_msc->stage = MSC_STAGE_IDLE;
-
- if (p_msc->complete_cb) {
+ if (p_msc->complete_cb != NULL) {
tuh_msc_complete_data_t const cb_data = {
.cbw = cbw,
.csw = csw,
.scsi_data = p_msc->buffer,
.user_arg = p_msc->complete_arg
};
- p_msc->complete_cb(dev_addr, &cb_data);
+ (void) p_msc->complete_cb(dev_addr, &cb_data);
}
break;
- // unknown state
default:
+ // unknown state
break;
}
@@ -378,22 +379,21 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_d
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
+uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
(void) rhport;
- TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
+ TU_VERIFY(MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass && MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol, 0);
// msc driver length is fixed
- uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
- desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
- TU_ASSERT(drv_len <= max_len);
+ const uint16_t drv_len =
+ (uint16_t)(sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ TU_ASSERT(drv_len <= max_len, 0);
- msch_interface_t* p_msc = get_itf(dev_addr);
- tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
+ msch_interface_t *p_msc = get_itf(dev_addr);
+ const tusb_desc_endpoint_t *ep_desc = (const tusb_desc_endpoint_t *)tu_desc_next(desc_itf);
for (uint32_t i = 0; i < 2; i++) {
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
- TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer, 0);
+ TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc), 0);
if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
p_msc->ep_in = ep_desc->bEndpointAddress;
@@ -406,7 +406,7 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* de
p_msc->itf_num = desc_itf->bInterfaceNumber;
- return true;
+ return drv_len;
}
bool msch_set_config(uint8_t daddr, uint8_t itf_num) {
@@ -501,7 +501,7 @@ static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_dat
// Capacity response field: Block size and Last LBA are both Big-Endian
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf;
- p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
+ p_msc->capacity[cbw->lun].block_count = (uint32_t) (tu_ntohl(resp->last_lba) + 1u);
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
// Mark enumeration is complete
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index b5fd55547..5dc7c0b94 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -30,7 +30,7 @@
#include "msc.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
//--------------------------------------------------------------------+
@@ -38,17 +38,17 @@
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MSC_MAXLUN
-#define CFG_TUH_MSC_MAXLUN 4
+ #define CFG_TUH_MSC_MAXLUN 4
#endif
typedef struct {
- msc_cbw_t const* cbw; // SCSI command
- msc_csw_t const* csw; // SCSI status
- void* scsi_data; // SCSI Data
- uintptr_t user_arg; // user argument
-}tuh_msc_complete_data_t;
+ const msc_cbw_t *cbw; // SCSI command
+ const msc_csw_t *csw; // SCSI status
+ void *scsi_data; // SCSI Data
+ uintptr_t user_arg; // user argument
+} tuh_msc_complete_data_t;
-typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
+typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data);
//--------------------------------------------------------------------+
// Application API
@@ -74,12 +74,14 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
// Complete callback is invoked when SCSI op is complete.
// return true if success, false if there is already pending operation.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
-bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_scsi_command(uint8_t daddr, const msc_cbw_t *cbw, void *data, tuh_msc_complete_cb_t complete_cb,
+ uintptr_t arg);
// Perform SCSI Inquiry command
// Complete callback is invoked when SCSI op is complete.
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
-bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t *response, tuh_msc_complete_cb_t complete_cb,
+ uintptr_t arg);
// Perform SCSI Test Unit Ready command
// Complete callback is invoked when SCSI op is complete.
@@ -88,23 +90,27 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
// Perform SCSI Request Sense 10 command
// Complete callback is invoked when SCSI op is complete.
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
-bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb,
+ uintptr_t arg);
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
// Complete callback is invoked when SCSI op is complete.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
-bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void *buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
// Complete callback is invoked when SCSI op is complete.
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
-bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, const void *buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read Capacity 10 command
// Complete callback is invoked when SCSI op is complete.
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
-bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t *response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
//------------- Application Callback -------------//
@@ -118,15 +124,15 @@ void tuh_msc_umount_cb(uint8_t dev_addr);
// Internal Class Driver API
//--------------------------------------------------------------------+
-bool msch_init (void);
-bool msch_deinit (void);
-bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
-bool msch_set_config (uint8_t daddr, uint8_t itf_num);
-void msch_close (uint8_t dev_addr);
-bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+bool msch_init(void);
+bool msch_deinit(void);
+uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
+bool msch_set_config(uint8_t daddr, uint8_t itf_num);
+void msch_close(uint8_t dev_addr);
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
- }
+}
#endif
#endif
diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h
index 40b6dd8b0..7b22837cd 100644
--- a/src/class/mtp/mtp.h
+++ b/src/class/mtp/mtp.h
@@ -798,45 +798,65 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_contain
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint16_t* utf16) {
- uint8_t count = 0;
- while (utf16[count]) {
+ uint32_t count = 0;
+ while (utf16[count] != 0u) {
count++;
}
- const uint32_t added_len = 1u + 2u * count;
- TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0);
+ // MTP strings store length in a single uint8_t, including trailing null.
+ TU_ASSERT(count < UINT8_MAX, 0);
+ count++;
+
uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t);
- *buf++ = count;
+ if (count == 1) {
+ // empty string (size only): single zero byte
+ TU_ASSERT(p_container->header->len + 1 < CFG_TUD_MTP_EP_BUFSIZE, 0);
+ *buf = 0;
+ p_container->header->len++;
+ return 1u;
+ }
+
+ const uint32_t added_len = 1u + count * 2u;
+ TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0);
+
+ *buf++ = (uint8_t) count;
p_container->header->len++;
- memcpy(buf, utf16, 2 * count);
- p_container->header->len += 2 * count;
+ memcpy(buf, utf16, 2u * count);
+ p_container->header->len += 2u * count;
return added_len;
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) {
- const uint8_t len = (uint8_t) (strlen(str) + 1); // include null
- TU_ASSERT(p_container->header->len + 1 + 2 * len < CFG_TUD_MTP_EP_BUFSIZE, 0);
+ const size_t cstr_len = strlen(str);
+ // MTP strings store length in a single uint8_t, including trailing null.
+ TU_ASSERT(cstr_len < UINT8_MAX, 0);
+
+ const uint32_t count = (uint32_t) cstr_len + 1u; // include null
uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t);
- if (len == 1) {
- // empty string (null only): single zero byte
+ if (count == 1u) {
+ // empty string (size only): single zero byte
+ TU_ASSERT(p_container->header->len + 1 < CFG_TUD_MTP_EP_BUFSIZE, 0);
*buf = 0;
p_container->header->len++;
- return 1;
- } else {
- *buf++ = len;
- p_container->header->len++;
+ return 1u;
+ }
+
+ const uint32_t added_len = 1u + 2u * count;
+ TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0);
- for (uint8_t i = 0; i < len; i++) {
- buf[0] = str[i];
- buf[1] = 0;
- buf += 2;
- p_container->header->len += 2;
- }
- return 1u + 2u * len;
+ *buf++ = (uint8_t) count;
+ p_container->header->len++;
+
+ for (uint32_t i = 0; i < count; i++) {
+ *buf++ = (uint8_t) str[i];
+ *buf++ = 0;
}
+ p_container->header->len += 2u * count;
+
+ return added_len;
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_container_info_t* p_container, uint8_t data) {
@@ -875,8 +895,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_conta
//
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) {
- uint8_t nchars = *buf++;
- memcpy(utf16, buf, 2 * nchars);
+ size_t nchars = *buf++;
+ memcpy(utf16, buf, 2u * nchars);
return 1u + 2u * nchars;
}
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
index 798a965eb..1f76dfcc7 100644
--- a/src/class/mtp/mtp_device.c
+++ b/src/class/mtp/mtp_device.c
@@ -92,8 +92,9 @@ typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
- uint8_t ep_event;
+ uint8_t ep_event;
+ uint8_t ep_sz_fs;
// Bulk Only Transfer (BOT) Protocol
uint8_t phase;
@@ -191,45 +192,50 @@ TU_ATTR_UNUSED static const char* _mtp_phase_str[] = {
//--------------------------------------------------------------------+
static bool prepare_new_command(mtpd_interface_t* p_mtp) {
p_mtp->phase = MTP_PHASE_COMMAND;
- return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE);
+ return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE, false);
}
-static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) {
- mtpd_interface_t* p_mtp = &_mtpd_itf;
+bool tud_mtp_data_send(mtp_container_info_t *p_container) {
+ mtpd_interface_t *p_mtp = &_mtpd_itf;
if (p_mtp->phase == MTP_PHASE_COMMAND) {
// 1st data block: header + payload
p_mtp->phase = MTP_PHASE_DATA;
p_mtp->xferred_len = 0;
+ p_mtp->total_len = p_container->header->len;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- p_mtp->total_len = p_container->header->len;
- p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK;
- p_container->header->transaction_id = p_mtp->command.header.transaction_id;
- p_mtp->io_header = *p_container->header; // save header for subsequent data
- } else {
- // OUT transfer: total length is at least max packet size
- p_mtp->total_len = tu_max32(p_container->header->len, CFG_TUD_MTP_EP_BUFSIZE);
- }
- } else {
- // subsequent data block: payload only
- TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA);
+ p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK;
+ p_container->header->transaction_id = p_mtp->command.header.transaction_id;
+ p_mtp->io_header = *p_container->header; // save header for subsequent data
}
- const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE);
+ const uint16_t xact_len = (uint16_t)tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE);
+
+ TU_LOG_DRV(" MTP Data IN: xferred_len/total_len=%lu/%lu, xact_len=%u\r\n", p_mtp->xferred_len, p_mtp->total_len,
+ xact_len);
if (xact_len) {
- // already transferred all bytes in header's length. Application make an unnecessary extra call
- TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, ep_addr));
- TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len));
+ TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_in));
+ TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, xact_len, false));
}
return true;
}
-bool tud_mtp_data_send(mtp_container_info_t* p_container) {
- return mtpd_data_xfer(p_container, _mtpd_itf.ep_in);
-}
+bool tud_mtp_data_receive(mtp_container_info_t *p_container) {
+ mtpd_interface_t *p_mtp = &_mtpd_itf;
+ if (p_mtp->phase == MTP_PHASE_COMMAND) {
+ // 1st data block: header + payload
+ p_mtp->phase = MTP_PHASE_DATA;
+ p_mtp->xferred_len = 0;
+ p_mtp->total_len = p_container->header->len;
+ }
+
+ // up to buffer size since 1st packet (with header) may also contain payload
+ const uint16_t xact_len = CFG_TUD_MTP_EP_BUFSIZE;
-bool tud_mtp_data_receive(mtp_container_info_t* p_container) {
- return mtpd_data_xfer(p_container, _mtpd_itf.ep_out);
+ TU_LOG_DRV(" MTP Data OUT: xferred_len/total_len=%lu/%lu, xact_len=%u\r\n", p_mtp->xferred_len, p_mtp->total_len,
+ xact_len);
+ TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_out));
+ TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, xact_len, false));
+ return true;
}
bool tud_mtp_response_send(mtp_container_info_t* p_container) {
@@ -238,7 +244,7 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container) {
p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK;
p_container->header->transaction_id = p_mtp->command.header.transaction_id;
TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_in));
- return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len);
+ return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t) p_container->header->len, false);
}
bool tud_mtp_mounted(void) {
@@ -251,7 +257,7 @@ bool tud_mtp_event_send(mtp_event_t* event) {
TU_VERIFY(p_mtp->ep_event != 0);
_mtpd_epbuf.buf_event = *event;
TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_event)); // Claim the endpoint
- return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t));
+ return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t), false);
}
//--------------------------------------------------------------------+
@@ -287,15 +293,20 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16
p_mtp->itf_num = itf_desc->bInterfaceNumber;
// Open interrupt IN endpoint
- const tusb_desc_endpoint_t* ep_desc = (const tusb_desc_endpoint_t*) tu_desc_next(itf_desc);
- TU_ASSERT(ep_desc->bDescriptorType == TUSB_DESC_ENDPOINT && ep_desc->bmAttributes.xfer == TUSB_XFER_INTERRUPT, 0);
- TU_ASSERT(usbd_edpt_open(rhport, ep_desc), 0);
- p_mtp->ep_event = ep_desc->bEndpointAddress;
+ const tusb_desc_endpoint_t* ep_desc_int = (const tusb_desc_endpoint_t*) tu_desc_next(itf_desc);
+ TU_ASSERT(ep_desc_int->bDescriptorType == TUSB_DESC_ENDPOINT && ep_desc_int->bmAttributes.xfer == TUSB_XFER_INTERRUPT, 0);
+ TU_ASSERT(usbd_edpt_open(rhport, ep_desc_int), 0);
+ p_mtp->ep_event = ep_desc_int->bEndpointAddress;
// Open endpoint pair
- TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0);
+ const tusb_desc_endpoint_t* ep_desc_bulk = (const tusb_desc_endpoint_t*) tu_desc_next(ep_desc_int);
+ TU_ASSERT(usbd_open_edpt_pair(rhport, (const uint8_t*)ep_desc_bulk, 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0);
TU_ASSERT(prepare_new_command(p_mtp), 0);
+ if (tud_speed_get() == TUSB_SPEED_FULL) {
+ p_mtp->ep_sz_fs = (uint8_t)tu_edpt_packet_size(ep_desc_bulk);
+ }
+
return mtpd_itf_size;
}
@@ -310,7 +321,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
.session_id = p_mtp->session_id,
.request = request,
.buf = p_mtp->control_buf,
- .bufsize = tu_le16toh(request->wLength),
+ .bufsize = request->wLength,
};
switch (request->bRequest) {
@@ -377,8 +388,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
mtp_generic_container_t* p_container = (mtp_generic_container_t*) _mtpd_epbuf.buf;
#if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL
- tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code);
- TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code),
+ const uint16_t code = (p_mtp->phase == MTP_PHASE_COMMAND) ? p_container->header.code : p_mtp->command.header.code;
+ TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, code),
_mtp_phase_str[p_mtp->phase]);
#endif
@@ -417,19 +428,42 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
}
case MTP_PHASE_DATA: {
- const uint16_t bulk_mps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64;
p_mtp->xferred_len += xferred_bytes;
cb_data.total_xferred_bytes = p_mtp->xferred_len;
- bool is_complete = false;
- // complete if ZLP or short packet or overflow
- if (xferred_bytes == 0 || // ZLP
- (xferred_bytes & (bulk_mps - 1)) || // short packet
- p_mtp->xferred_len > p_mtp->total_len) {
- is_complete = true;
+ const bool is_data_in = (ep_addr == p_mtp->ep_in);
+ // For IN endpoint, threshold is bulk max packet size
+ // For OUT endpoint, threshold is endpoint buffer size, since we always queue fixed size
+ uint16_t threshold;
+ if (is_data_in) {
+ threshold = (p_mtp->ep_sz_fs > 0) ? p_mtp->ep_sz_fs : 512; // full speed bulk if set
+ } else {
+ threshold = CFG_TUD_MTP_EP_BUFSIZE;
+ }
+
+ // Check completion for IN and OUT separately
+ bool is_complete;
+ if (is_data_in) {
+ // IN completion: short packet, ZLP, or reaching total_len
+ is_complete = (xferred_bytes == 0 || xferred_bytes < threshold || p_mtp->xferred_len >= p_mtp->total_len);
+ } else {
+ // OUT completion: reaching total_len or ZLP only. A short packet does NOT end the phase
+ // (an early short packet before total_len is the cancel case, not normal completion).
+ is_complete = (p_mtp->xferred_len >= p_mtp->total_len) || ((xferred_bytes == 0 && p_mtp->xferred_len > 0));
+ }
+
+ TU_LOG_DRV(" MTP Data %s CB: xferred_bytes=%lu, xferred_len/total_len=%lu/%lu, is_complete=%d\r\n",
+ is_data_in ? "IN" : "OUT", xferred_bytes, p_mtp->xferred_len, p_mtp->total_len, is_complete ? 1 : 0);
+
+ // Send/queue ZLP if packet is full-sized but transfer is complete
+ if (is_complete && xferred_bytes > 0 && !(xferred_bytes & (threshold - 1))) {
+ TU_LOG_DRV(" queue ZLP\r\n");
+ TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, ep_addr));
+ TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, NULL, 0, false));
+ return true;
}
- if (ep_addr == p_mtp->ep_in) {
+ if (is_data_in) {
// Data In
if (is_complete) {
cb_data.io_container.header->len = sizeof(mtp_container_header_t);
diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h
index 397fbbbce..f2c5cef7f 100644
--- a/src/class/mtp/mtp_device.h
+++ b/src/class/mtp/mtp_device.h
@@ -18,7 +18,7 @@
* 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 IN0
+ * 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.
@@ -53,12 +53,14 @@ typedef struct {
typedef struct {
uint8_t idx;
uint8_t stage; // control stage
- uint32_t session_id;
- const tusb_control_request_t* request;
// buffer for data stage
- uint8_t* buf;
uint16_t bufsize;
+ uint8_t* buf;
+
+ const tusb_control_request_t* request;
+
+ uint32_t session_id;
} tud_mtp_request_cb_data_t;
// Number of supported operations, events, device properties, capture formats, playback formats
@@ -76,9 +78,13 @@ typedef struct {
mtp_auint16_t(_capture_count) capture_formats; \
mtp_auint16_t(_playback_count) playback_formats; \
/* string fields will be added using append function */ \
+ /* mtp_string_t() Manufacturer */ \
+ /* mtp_string_t() Model */ \
+ /* mtp_string_t() Device Version */ \
+ /* mtp_string_t() Serial Number */ \
}
-typedef MTP_DEVICE_INFO_STRUCT(
+typedef MTP_DEVICE_INFO_STRUCT( //-V2586 [MISRA-C-18.7] Flexible array members should not be declared
sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS),
TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES),
TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS)
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index 299eb97c8..643bcfbcd 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -35,8 +35,6 @@
#include "net_device.h"
#include "rndis_protocol.h"
-extern void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
-
#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
@@ -50,9 +48,10 @@ typedef struct {
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
- uint8_t ep_notif;
uint8_t ep_in;
uint8_t ep_out;
+ uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal)
+ uint8_t ep_notif;
bool ecm_mode;
@@ -83,13 +82,20 @@ CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
static bool can_xmit;
static bool ecm_link_is_up = true; // Store link state for ECM mode
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_network_set_packet_filter_cb(uint16_t packet_filter) {
+ (void) packet_filter;
+}
+
void tud_network_recv_renew(void) {
- usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
+ usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE, false);
}
static void do_in_xfer(uint8_t *buf, uint16_t len) {
can_xmit = false;
- usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len);
+ usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len, false);
}
void netd_report(uint8_t *buf, uint16_t len) {
@@ -102,7 +108,7 @@ void netd_report(uint8_t *buf, uint16_t len) {
}
memcpy(_netd_epbuf.notify, buf, len);
- usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
+ usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len, false);
}
//--------------------------------------------------------------------+
@@ -178,6 +184,9 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+ // Save the actual bulk endpoint size (IN and OUT assumed equal)
+ _netd_itf.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc);
+
if (_netd_itf.ecm_mode) {
// ECM by default is in-active, save the endpoint attribute
// to open later when received setInterface
@@ -208,14 +217,15 @@ static void ecm_report(bool nc) {
},
};
+ const uint32_t link_bps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 480000000U : 12000000U;
const ecm_notify_t ecm_notify_csc = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */
.wLength = 8,
},
- .downlink = 9728000,
- .uplink = 9728000,
+ .downlink = link_bps,
+ .uplink = link_bps,
};
ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc;
@@ -287,6 +297,7 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
if (_netd_itf.ecm_mode) {
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
+ tud_network_set_packet_filter_cb(request->wValue);
tud_control_xfer(rhport, request, NULL, 0);
// Only send connection notification if link is up
if (ecm_link_is_up) {
@@ -358,8 +369,7 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
/* data transmission finished */
if (ep_addr == _netd_itf.ep_in) {
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
-
- if (xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE))) {
+ if (xferred_bytes > 0 && 0 == (xferred_bytes & (_netd_itf.ep_size-1))) {
do_in_xfer(NULL, 0); /* a ZLP is needed */
} else {
/* we're finally finished */
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 0245a87f2..27ff89b72 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_NCM_H_
-#define _TUSB_NCM_H_
+#ifndef TUSB_NCM_H_
+#define TUSB_NCM_H_
#include "common/tusb_common.h"
@@ -161,4 +161,18 @@ typedef struct {
uint32_t uplink;
} ncm_notify_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t bcdNcmVersion;
+ uint8_t bmCapabilities;
+} tusb_desc_cdc_ncm_func_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t dwNtbInMaxSize;
+ uint16_t wNtbInMaxDatagrams;
+ uint16_t wReserved;
+} ncm_ntb_input_size_t;
+
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index 02833c5f1..e5f441300 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -50,10 +50,6 @@
#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-
#include "device/usbd.h"
#include "device/usbd_pvt.h"
@@ -87,17 +83,28 @@ typedef struct {
uint8_t itf_num; // interface number
uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
uint8_t rhport; // storage of \a rhport because some callbacks are done without it
+ uint16_t ep_size; // bulk endpoint max packet size (IN and OUT assumed equal)
// recv handling
recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
- recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic
+ recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic (circular buffer)
+ #if RECV_NTB_N > 1
+ uint8_t recv_ready_head; // head index for recv_ready_ntb circular buffer
+ uint8_t recv_ready_tail; // tail index for recv_ready_ntb circular buffer
+ uint8_t recv_ready_count; // number of elements in recv_ready_ntb circular buffer
+ #endif
recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver
recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic
uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
// xmit handling
xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs
- xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB
+ xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB (circular buffer)
+ #if XMIT_NTB_N > 1
+ uint8_t xmit_ready_head; // head index for xmit_ready_ntb circular buffer
+ uint8_t xmit_ready_tail; // tail index for xmit_ready_ntb circular buffer
+ uint8_t xmit_ready_count; // number of elements in xmit_ready_ntb circular buffer
+ #endif
xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB
xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver
uint16_t xmit_sequence; // NTB sequence counter
@@ -112,6 +119,12 @@ typedef struct {
bool notification_xmit_is_running; // notification is currently transmitted
bool link_is_up; // current link state
+ // host-configured transmit limits
+ uint8_t bm_capabilities;
+ uint16_t xmit_max_ntb_size; // maximum NTB size device may send
+ uint16_t xmit_max_datagrams; // maximum datagrams per NTB device may send
+ ncm_ntb_input_size_t ntb_input_size;
+
// misc
bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again
@@ -132,6 +145,21 @@ typedef struct {
static ncm_interface_t ncm_interface;
CFG_TUD_MEM_SECTION static ncm_epbuf_t ncm_epbuf;
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_network_set_packet_filter_cb(uint16_t packet_filter) {
+ (void) packet_filter;
+}
+
+TU_ATTR_WEAK bool tud_network_default_link_state_cb(void) {
+ #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
+ return CFG_TUD_NCM_DEFAULT_LINK_UP;
+ #else
+ return true;
+ #endif
+}
+
/**
* This is the NTB parameter structure
*
@@ -205,7 +233,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
uint16_t notif_len = sizeof(notify_speed_change.header) + notify_speed_change.header.wLength;
ncm_epbuf.epnotif = notify_speed_change;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_epbuf.epnotif, notif_len);
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_epbuf.epnotif, notif_len, false);
ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
ncm_interface.notification_xmit_is_running = true;
@@ -227,7 +255,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
uint16_t notif_len = sizeof(notify_connected.header) + notify_connected.header.wLength;
ncm_epbuf.epnotif = notify_connected;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_epbuf.epnotif, notif_len);
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_epbuf.epnotif, notif_len, false);
ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
ncm_interface.notification_xmit_is_running = true;
@@ -283,13 +311,17 @@ static xmit_ntb_t *xmit_get_free_ntb(void) {
static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
- for (int i = 0; i < XMIT_NTB_N; ++i) {
- if (ncm_interface.xmit_ready_ntb[i] == NULL) {
- ncm_interface.xmit_ready_ntb[i] = ready_ntb;
- return;
- }
+#if XMIT_NTB_N == 1
+ ncm_interface.xmit_ready_ntb[0] = ready_ntb;
+#else
+ if (ncm_interface.xmit_ready_count >= XMIT_NTB_N) {
+ TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
+ return;
}
- TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
+ ncm_interface.xmit_ready_ntb[ncm_interface.xmit_ready_head] = ready_ntb;
+ ncm_interface.xmit_ready_head = (ncm_interface.xmit_ready_head + 1) % XMIT_NTB_N;
+ ncm_interface.xmit_ready_count++;
+#endif
} // xmit_put_ntb_into_ready_list
/**
@@ -297,14 +329,23 @@ static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
* If the ready list is empty, return NULL.
*/
static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
- xmit_ntb_t *r = NULL;
+#if XMIT_NTB_N == 1
+ xmit_ntb_t *r = ncm_interface.xmit_ready_ntb[0];
+ ncm_interface.xmit_ready_ntb[0] = NULL;
+ TU_LOG_DRV("xmit_get_next_ready_ntb: %p\n", r);
+ return r;
+#else
+ if (ncm_interface.xmit_ready_count == 0) {
+ return NULL; // empty
+ }
- r = ncm_interface.xmit_ready_ntb[0];
- memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
- ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
+ xmit_ntb_t *r = ncm_interface.xmit_ready_ntb[ncm_interface.xmit_ready_tail];
+ ncm_interface.xmit_ready_tail = (ncm_interface.xmit_ready_tail + 1) % XMIT_NTB_N;
+ ncm_interface.xmit_ready_count--;
- TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ TU_LOG_DRV("xmit_get_next_ready_ntb: %p\n", r);
return r;
+#endif
} // xmit_get_next_ready_ntb
/**
@@ -321,7 +362,8 @@ static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
- if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+ uint16_t const ep_size = ncm_interface.ep_size;
+ if (xferred_bytes == 0 || (xferred_bytes & (ep_size-1)) != 0) {
return false;
}
@@ -331,7 +373,7 @@ static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
// start transmission of the ZLP
- usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
+ usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0, false);
return true;
} // xmit_insert_required_zlp
@@ -377,7 +419,7 @@ static void xmit_start_if_possible(uint8_t rhport) {
}
// Kick off an endpoint transfer
- usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
+ usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength, false);
} // xmit_start_if_possible
/**
@@ -389,10 +431,10 @@ static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size
if (ncm_interface.xmit_glue_ntb == NULL) {
return false;
}
- if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx >= ncm_interface.xmit_max_datagrams) {
return false;
}
- if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_IN_NTB_MAX_SIZE) {
+ if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + (uint32_t)XMIT_ALIGN_OFFSET(datagram_size) > (uint32_t)ncm_interface.xmit_max_ntb_size) {
return false;
}
return true;
@@ -462,14 +504,23 @@ static recv_ntb_t *recv_get_free_ntb(void) {
* If the ready list is empty, return NULL.
*/
static recv_ntb_t *recv_get_next_ready_ntb(void) {
- recv_ntb_t *r = NULL;
+#if RECV_NTB_N == 1
+ recv_ntb_t *r = ncm_interface.recv_ready_ntb[0];
+ ncm_interface.recv_ready_ntb[0] = NULL;
+ TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+#else
+ if (ncm_interface.recv_ready_count == 0) {
+ return NULL; // empty
+ }
- r = ncm_interface.recv_ready_ntb[0];
- memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
- ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
+ recv_ntb_t *r = ncm_interface.recv_ready_ntb[ncm_interface.recv_ready_tail];
+ ncm_interface.recv_ready_tail = (ncm_interface.recv_ready_tail + 1) % RECV_NTB_N;
+ ncm_interface.recv_ready_count--;
TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
return r;
+#endif
} // recv_get_next_ready_ntb
/**
@@ -494,13 +545,17 @@ static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) {
static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) {
TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
- for (int i = 0; i < RECV_NTB_N; ++i) {
- if (ncm_interface.recv_ready_ntb[i] == NULL) {
- ncm_interface.recv_ready_ntb[i] = ready_ntb;
- return;
- }
+#if RECV_NTB_N == 1
+ ncm_interface.recv_ready_ntb[0] = ready_ntb;
+#else
+ if (ncm_interface.recv_ready_count >= RECV_NTB_N) {
+ TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
+ return;
}
- TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
+ ncm_interface.recv_ready_ntb[ncm_interface.recv_ready_head] = ready_ntb;
+ ncm_interface.recv_ready_head = (ncm_interface.recv_ready_head + 1) % RECV_NTB_N;
+ ncm_interface.recv_ready_count++;
+#endif
} // recv_put_ntb_into_ready_list
/**
@@ -526,7 +581,7 @@ static void recv_try_to_start_new_reception(uint8_t rhport) {
// initiate transfer
TU_LOG_DRV(" start reception\n");
- bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE, false);
if (!r) {
recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
ncm_interface.recv_tinyusb_ntb = NULL;
@@ -676,7 +731,7 @@ static void recv_transfer_datagram_to_glue_logic(void) {
bool tud_network_can_xmit(uint16_t size) {
TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
- TU_ASSERT(size <= CFG_TUD_NCM_IN_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
+ TU_ASSERT(size <= ncm_interface.xmit_max_ntb_size - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
// -> everything is fine
@@ -776,8 +831,8 @@ void tud_network_link_state(uint8_t rhport, bool is_up) {
return;
}
- // Reset notification state to send link state update
- ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
+ // Reset notification state to send speed change notification first, then link state notification
+ ncm_interface.notification_xmit_state = NOTIFICATION_SPEED;
// Trigger notification transmission
notification_xmit(rhport, false);
@@ -796,18 +851,16 @@ void netd_init(void) {
memset(&ncm_interface, 0, sizeof(ncm_interface));
+ ncm_interface.xmit_max_ntb_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
+ ncm_interface.xmit_max_datagrams = CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB;
+
for (int i = 0; i < XMIT_NTB_N; ++i) {
ncm_interface.xmit_free_ntb[i] = &ncm_epbuf.xmit[i].ntb;
}
for (int i = 0; i < RECV_NTB_N; ++i) {
ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
}
- // Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP
- #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
- ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP;
- #else
- ncm_interface.link_is_up = true; // Default to link up if not set.
- #endif
+ ncm_interface.link_is_up = tud_network_default_link_state_cb();
} // netd_init
/**
@@ -846,10 +899,14 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16
ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
- // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const *p_desc = tu_desc_next(itf_desc);
while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
+ if (tu_desc_subtype(p_desc) == CDC_FUNC_DESC_NCM) {
+ TU_ASSERT(tu_desc_len(p_desc) >= sizeof(tusb_desc_cdc_ncm_func_t), 0);
+ tusb_desc_cdc_ncm_func_t const *ncm_func = (tusb_desc_cdc_ncm_func_t const *) p_desc;
+ ncm_interface.bm_capabilities = ncm_func->bmCapabilities;
+ }
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
@@ -873,6 +930,7 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16
// a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
+ ncm_interface.ep_size = tu_edpt_packet_size((tusb_desc_endpoint_t const *) p_desc);
drv_len += 2 * sizeof(tusb_desc_endpoint_t);
return drv_len;
@@ -922,12 +980,12 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
* At startup transmission of notification packets are done here.
*/
bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
- if (stage != CONTROL_STAGE_SETUP) {
- return true;
- }
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
switch (request->bRequest) {
case TUSB_REQ_GET_INTERFACE: {
@@ -944,6 +1002,9 @@ bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
if (ncm_interface.itf_data_alt == 1) {
tud_network_recv_renew_r(rhport);
notification_xmit(rhport, false);
+ } else {
+ // Reset notification state to send link state update when interface is re-activated
+ ncm_interface.notification_xmit_state = NOTIFICATION_SPEED;
}
tud_control_status(rhport, request);
} break;
@@ -958,16 +1019,75 @@ bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
switch (request->bRequest) {
case NCM_GET_NTB_PARAMETERS: {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
// transfer NTB parameters to host.
tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
} break;
- // unsupported request
+ case NCM_SET_ETHERNET_PACKET_FILTER: {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ // Some hosts issue this request even if ETH_FILTER is not advertised,
+ // see https://bugzilla.kernel.org/show_bug.cgi?id=217290
+
+ tud_network_set_packet_filter_cb(request->wValue);
+ tud_control_xfer(rhport, request, NULL, 0);
+ } break;
+
+ case NCM_GET_NTB_INPUT_SIZE: {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ TU_VERIFY(request->wLength >=4, false);
+
+ uint8_t resp_len = (request->wLength >= 8 && (ncm_interface.bm_capabilities & NCM_NETWORK_CAPS_NTB_INPUT_SIZE)) ? 8 : 4;
+
+ ncm_ntb_input_size_t ntb_input_size = {
+ .dwNtbInMaxSize = ncm_interface.xmit_max_ntb_size,
+ .wNtbInMaxDatagrams = ncm_interface.xmit_max_datagrams
+ };
+ tud_control_xfer(rhport, request, &ntb_input_size, resp_len);
+ } break;
+
+ case NCM_SET_NTB_INPUT_SIZE: {
+ if (stage == CONTROL_STAGE_SETUP) {
+ /* wLength == 8 -> the NTB Input Size Structure (if NCM_NETWORK_CAPS_NTB_INPUT_SIZE is set)
+ wLength == 4 -> dwNtbInMaxSize field of the NTB Input Size Structure. */
+ TU_VERIFY(request->wLength == 4 || request->wLength == 8, false);
+ if (request->wLength == 8) {
+ TU_VERIFY(ncm_interface.bm_capabilities & NCM_NETWORK_CAPS_NTB_INPUT_SIZE, false);
+ }
+
+ tu_memclr(&ncm_interface.ntb_input_size, sizeof(ncm_interface.ntb_input_size));
+ tud_control_xfer(rhport, request, &ncm_interface.ntb_input_size, request->wLength);
+ } else if (stage == CONTROL_STAGE_DATA) {
+ /* CDC-NCM 1.0 Table 6-4, up to NTB16 size */
+ const uint32_t requested_size = ncm_interface.ntb_input_size.dwNtbInMaxSize;
+ if (requested_size < 2048u || requested_size > 65535u) {
+ return false;
+ }
+ ncm_interface.xmit_max_ntb_size = tu_min16(requested_size, CFG_TUD_NCM_IN_NTB_MAX_SIZE);
+
+ if (ncm_interface.ntb_input_size.wNtbInMaxDatagrams == 0 || ncm_interface.ntb_input_size.wNtbInMaxDatagrams > CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
+ ncm_interface.xmit_max_datagrams = CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB;
+ } else {
+ ncm_interface.xmit_max_datagrams = ncm_interface.ntb_input_size.wNtbInMaxDatagrams;
+ }
+ }
+ } break;
+
+ // unsupported request
default:
return false;
}
break;
- // unsupported request
+
+ // unsupported request
default:
return false;
}
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index fff2623b7..1ad069d92 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_NET_DEVICE_H_
-#define _TUSB_NET_DEVICE_H_
+#ifndef TUSB_NET_DEVICE_H_
+#define TUSB_NET_DEVICE_H_
#include <stdint.h>
#include "class/cdc/cdc.h"
@@ -35,9 +35,6 @@
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
#endif
-/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
-#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-
/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */
#ifndef CFG_TUD_NET_MTU
#define CFG_TUD_NET_MTU 1514
@@ -50,12 +47,29 @@ typedef enum
NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
} ncm_data_interface_protocol_code_t;
+// Table 5.2 bmNetworkCapabilities bits
+typedef enum {
+ NCM_NETWORK_CAPS_NONE = 0x00,
+ NCM_NETWORK_CAPS_ETH_FILTER = (1 << 0),
+ NCM_NETWORK_CAPS_NET_ADDRESS = (1 << 1),
+ NCM_NETWORK_CAPS_ENCAP_COMMAND = (1 << 2),
+ NCM_NETWORK_CAPS_MAX_DATAGRAM_SIZE = (1 << 3),
+ NCM_NETWORK_CAPS_CRC_MODE = (1 << 4),
+ NCM_NETWORK_CAPS_NTB_INPUT_SIZE = (1 << 5)
+} ncm_network_capabilities_t;
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
+// Implemented by Application
+//--------------------------------------------------------------------+
+#if CFG_TUD_ECM_RNDIS
+extern void rndis_class_set_handler(uint8_t *data, int size);
+#endif
+
+//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
@@ -89,6 +103,13 @@ extern uint8_t tud_network_mac_address[6];
//------------- NCM -------------//
+// Optional callback: informs the application about host requested packet filter bits
+void tud_network_set_packet_filter_cb(uint16_t packet_filter);
+
+// Optional callback: called during netd_init() to get the initial link state.
+// Override to return the actual physical link state instead of the compile-time default.
+bool tud_network_default_link_state_cb(void);
+
// Set the network link state (up/down) and notify the host
void tud_network_link_state(uint8_t rhport, bool is_up);
@@ -107,4 +128,4 @@ void netd_report (uint8_t *buf, uint16_t len);
}
#endif
-#endif /* _TUSB_NET_DEVICE_H_ */
+#endif /* TUSB_NET_DEVICE_H_ */
diff --git a/src/class/cdc/cdc_rndis_host.h b/src/class/printer/printer.h
index bb431ec1f..09d1a8956 100644
--- a/src/class/cdc/cdc_rndis_host.h
+++ b/src/class/printer/printer.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2026 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,40 +24,39 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup CDC_RNDIS
- * \defgroup CDC_RNSID_Host Host
- * @{ */
-
-#ifndef _TUSB_CDC_RNDIS_HOST_H_
-#define _TUSB_CDC_RNDIS_HOST_H_
+#ifndef TUSB_PRINTER_H_
+#define TUSB_PRINTER_H_
#include "common/tusb_common.h"
-#include "host/usbh.h"
-#include "cdc_rndis.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-//--------------------------------------------------------------------+
-// INTERNAL RNDIS-CDC Driver API
-//--------------------------------------------------------------------+
-typedef struct {
- OSAL_SEM_DEF(semaphore_notification);
- osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe
- uint32_t max_xfer_size; // got from device's msg initialize complete
- uint8_t mac_address[6];
-}rndish_data_t;
+/// Printer Class Specific Control Request
+typedef enum {
+ TUSB_PRINTER_REQUEST_GET_DEVICE_ID = 0x00, ///< Get device ID
+ TUSB_PRINTER_REQUEST_GET_PORT_STATUS = 0x01, ///< Get port status
+ TUSB_PRINTER_REQUEST_SOFT_RESET = 0x02, ///< Soft reset
+} tusb_printer_request_type_t;
+
+/// Printer Port Status (returned by GET_PORT_STATUS request)
+/// USB Printer Class spec 1.1, Section 4.2
+typedef union TU_ATTR_PACKED {
+ uint8_t status;
+ struct TU_ATTR_PACKED {
+ uint8_t reserved0 : 3; ///< Reserved (bits 0-2)
+ uint8_t not_error : 1; ///< 1 = no error, 0 = error
+ uint8_t selected : 1; ///< 1 = selected (online), 0 = not selected
+ uint8_t paper_empty : 1; ///< 1 = paper empty, 0 = paper not empty
+ uint8_t reserved6 : 2; ///< Reserved (bits 6-7)
+ } status_bm;
+} tusb_printer_port_status_t;
-void rndish_init(void);
-bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
-void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
-void rndish_close(uint8_t dev_addr);
+TU_VERIFY_STATIC(sizeof(tusb_printer_port_status_t) == 1, "size is not correct");
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_CDC_RNDIS_HOST_H_ */
-
-/** @} */
+#endif
diff --git a/src/class/printer/printer_device.c b/src/class/printer/printer_device.c
new file mode 100644
index 000000000..158455fc9
--- /dev/null
+++ b/src/class/printer/printer_device.c
@@ -0,0 +1,328 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ */
+
+#include "tusb_option.h"
+
+#if (CFG_TUD_ENABLED && CFG_TUD_PRINTER)
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "printer_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint8_t itf_num;
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+ tu_edpt_stream_t rx_stream;
+ tu_edpt_stream_t tx_stream;
+
+ uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE];
+} printer_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(printer_interface_t, rx_stream)
+
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_PRINTER_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_PRINTER_TX_EPSIZE);
+} printer_epbuf_t;
+
+CFG_TUD_MEM_SECTION static printer_epbuf_t _printer_epbuf[CFG_TUD_PRINTER];
+#endif
+
+static printer_interface_t _printer_itf[CFG_TUD_PRINTER];
+
+//--------------------------------------------------------------------+
+// INTERNAL HELPERS
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t _find_itf(uint8_t ep_addr) {
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ const printer_interface_t *p = &_printer_itf[i];
+ if (ep_addr == p->rx_stream.ep_addr || ep_addr == p->tx_stream.ep_addr) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_printer_rx_cb(uint8_t itf) {
+ (void)itf;
+}
+
+TU_ATTR_WEAK void tud_printer_tx_complete_cb(uint8_t itf) {
+ (void)itf;
+}
+
+TU_ATTR_WEAK void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request) {
+ (void)itf;
+ (void)request;
+}
+
+TU_ATTR_WEAK uint8_t const *tud_printer_get_device_id_cb(uint8_t itf) {
+ (void)itf;
+ return NULL;
+}
+
+TU_ATTR_WEAK uint8_t tud_printer_get_port_status_cb(uint8_t itf) {
+ (void)itf;
+ return 0x18; // not error, selected, paper not empty
+}
+
+TU_ATTR_WEAK void tud_printer_soft_reset_cb(uint8_t itf) {
+ (void)itf;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+uint32_t tud_printer_n_read_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_read_available(&_printer_itf[itf].rx_stream);
+}
+
+uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_read(&_printer_itf[itf].rx_stream, buffer, bufsize);
+}
+
+bool tud_printer_n_peek(uint8_t itf, uint8_t *chr) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+ return tu_edpt_stream_peek(&_printer_itf[itf].rx_stream, chr);
+}
+
+void tud_printer_n_read_flush(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, );
+ printer_interface_t *p = &_printer_itf[itf];
+ tu_edpt_stream_clear(&p->rx_stream);
+ tu_edpt_stream_read_xfer(&p->rx_stream);
+}
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write(&_printer_itf[itf].tx_stream, buffer, bufsize);
+}
+
+uint32_t tud_printer_n_write_flush(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write_xfer(&_printer_itf[itf].tx_stream);
+}
+
+uint32_t tud_printer_n_write_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER, 0);
+ return tu_edpt_stream_write_available(&_printer_itf[itf].tx_stream);
+}
+
+bool tud_printer_n_write_clear(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+ tu_edpt_stream_clear(&_printer_itf[itf].tx_stream);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// USBD-CLASS API
+//--------------------------------------------------------------------+
+void printerd_init(void) {
+ tu_memclr(_printer_itf, sizeof(_printer_itf));
+
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ printer_interface_t *p = &_printer_itf[i];
+
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ uint8_t *epout_buf = _printer_epbuf[i].epout;
+ uint8_t *epin_buf = _printer_epbuf[i].epin;
+ #endif
+
+ tu_edpt_stream_init(&p->rx_stream, false, false, false,
+ p->rx_ff_buf, CFG_TUD_PRINTER_RX_BUFSIZE, epout_buf);
+
+ tu_edpt_stream_init(&p->tx_stream, false, true, true,
+ p->tx_ff_buf, CFG_TUD_PRINTER_TX_BUFSIZE, epin_buf);
+ }
+}
+
+bool printerd_deinit(void) {
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ printer_interface_t *p = &_printer_itf[i];
+ tu_edpt_stream_deinit(&p->rx_stream);
+ tu_edpt_stream_deinit(&p->tx_stream);
+ }
+ return true;
+}
+
+void printerd_reset(uint8_t rhport) {
+ (void)rhport;
+
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ printer_interface_t *p = &_printer_itf[i];
+ tu_memclr(p, ITF_MEM_RESET_SIZE);
+ tu_edpt_stream_close(&p->rx_stream);
+ tu_edpt_stream_close(&p->tx_stream);
+ }
+}
+
+uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ TU_VERIFY(TUSB_CLASS_PRINTER == itf_desc->bInterfaceClass, 0);
+
+ // Find available interface slot
+ uint8_t const printer_id = _find_itf(0);
+ TU_ASSERT(printer_id < CFG_TUD_PRINTER, 0);
+ printer_interface_t *p = &_printer_itf[printer_id];
+
+ p->itf_num = itf_desc->bInterfaceNumber;
+
+ //------------- Endpoints -------------//
+ const uint8_t *p_desc = (const uint8_t *)itf_desc;
+ const uint8_t *desc_end = p_desc + max_len;
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+
+ p_desc = tu_desc_next(itf_desc);
+ for (uint8_t e = 0; e < itf_desc->bNumEndpoints; e++) {
+ TU_VERIFY(tu_desc_in_bounds(p_desc, desc_end), 0);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
+
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p->tx_stream;
+ tu_edpt_stream_open(stream_tx, rhport, desc_ep, CFG_TUD_PRINTER_TX_EPSIZE);
+ tu_edpt_stream_clear(stream_tx);
+ } else {
+ tu_edpt_stream_t *stream_rx = &p->rx_stream;
+ tu_edpt_stream_open(stream_rx, rhport, desc_ep, tu_edpt_packet_size(desc_ep));
+ tu_edpt_stream_clear(stream_rx);
+ TU_ASSERT(tu_edpt_stream_read_xfer(stream_rx) > 0, 0);
+ }
+
+ drv_len += sizeof(tusb_desc_endpoint_t);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ return drv_len;
+}
+
+bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request) {
+ TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ // GET_DEVICE_ID: wIndex = (interface_number << 8) | alt_setting
+ // GET_PORT_STATUS / SOFT_RESET: wIndex = interface_number
+ uint8_t itf_num;
+ if (TUSB_PRINTER_REQUEST_GET_DEVICE_ID == request->bRequest) {
+ itf_num = tu_u16_high(request->wIndex);
+ } else {
+ itf_num = tu_u16_low(request->wIndex);
+ }
+
+ // Find the printer instance index from the USB interface number
+ uint8_t itf = TUSB_INDEX_INVALID_8;
+ for (uint8_t i = 0; i < CFG_TUD_PRINTER; i++) {
+ if (_printer_itf[i].itf_num == itf_num) {
+ itf = i;
+ break;
+ }
+ }
+ TU_VERIFY(itf < CFG_TUD_PRINTER);
+
+ // https://www.usb.org/sites/default/files/usbprint11a021811.pdf
+ if (stage == CONTROL_STAGE_SETUP) {
+ switch (request->bRequest) {
+ case TUSB_PRINTER_REQUEST_GET_DEVICE_ID: {
+ const uint8_t *device_id = tud_printer_get_device_id_cb(itf);
+ TU_VERIFY(device_id);
+ const uint16_t total_len = (uint16_t)((device_id[0] << 8) | device_id[1]);
+ return tud_control_xfer(rhport, request, (void *)(uintptr_t)device_id, total_len);
+ }
+
+ case TUSB_PRINTER_REQUEST_GET_PORT_STATUS: {
+ static uint8_t port_status;
+ port_status = tud_printer_get_port_status_cb(itf);
+ return tud_control_xfer(rhport, request, &port_status, sizeof(port_status));
+ }
+
+ case TUSB_PRINTER_REQUEST_SOFT_RESET:
+ tud_printer_soft_reset_cb(itf);
+ tud_control_status(rhport, request);
+ return true;
+
+ default:
+ return false;
+ }
+ } else if (stage == CONTROL_STAGE_ACK) {
+ tud_printer_request_complete_cb(itf, request);
+ }
+
+ return true;
+}
+
+bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void)rhport;
+ (void)result;
+
+ uint8_t const itf = _find_itf(ep_addr);
+ TU_ASSERT(itf < CFG_TUD_PRINTER);
+ printer_interface_t *p = &_printer_itf[itf];
+
+ // Received new data
+ if (ep_addr == p->rx_stream.ep_addr) {
+ tu_edpt_stream_read_xfer_complete(&p->rx_stream, xferred_bytes);
+
+ if (!tu_edpt_stream_empty(&p->rx_stream)) {
+ tud_printer_rx_cb(itf);
+ }
+
+ tu_edpt_stream_read_xfer(&p->rx_stream);
+ }
+
+ // Data sent to host
+ if (ep_addr == p->tx_stream.ep_addr) {
+ tud_printer_tx_complete_cb(itf);
+
+ if (0 == tu_edpt_stream_write_xfer(&p->tx_stream)) {
+ tu_edpt_stream_write_zlp_if_needed(&p->tx_stream, xferred_bytes);
+ }
+ }
+
+ return true;
+}
+
+#endif
diff --git a/src/class/printer/printer_device.h b/src/class/printer/printer_device.h
new file mode 100644
index 000000000..afde1f022
--- /dev/null
+++ b/src/class/printer/printer_device.h
@@ -0,0 +1,151 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Ha Thach (tinyusb.org)
+ *
+ * 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_PRINTER_DEVICE_H_
+#define TUSB_PRINTER_DEVICE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "printer.h"
+
+//--------------------------------------------------------------------+
+// Configuration
+//--------------------------------------------------------------------+
+#ifndef CFG_TUD_PRINTER_RX_EPSIZE
+ #define CFG_TUD_PRINTER_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+#ifndef CFG_TUD_PRINTER_TX_EPSIZE
+ #define CFG_TUD_PRINTER_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Ports) i.e. CFG_TUD_PRINTER > 1
+//--------------------------------------------------------------------+
+
+// Get the number of bytes available for reading
+uint32_t tud_printer_n_read_available(uint8_t itf);
+
+// Read received bytes
+uint32_t tud_printer_n_read(uint8_t itf, void *buffer, uint32_t bufsize);
+
+// Get the number of bytes available for writing
+uint32_t tud_printer_n_write_available(uint8_t itf);
+
+// Clear the received FIFO
+void tud_printer_n_read_flush(uint8_t itf);
+
+// Get a byte from FIFO without removing it
+bool tud_printer_n_peek(uint8_t itf, uint8_t *ui8);
+
+// Write data to host
+uint32_t tud_printer_n_write(uint8_t itf, const void *buffer, uint32_t bufsize);
+
+// Force sending data in the TX FIFO
+uint32_t tud_printer_n_write_flush(uint8_t itf);
+
+// Clear the transmit FIFO
+bool tud_printer_n_write_clear(uint8_t itf);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read_available(void) {
+ return tud_printer_n_read_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_available(void) {
+ return tud_printer_n_write_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_read(void *buffer, uint32_t bufsize) {
+ return tud_printer_n_read(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_printer_read_flush(void) {
+ tud_printer_n_read_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_peek(uint8_t *ui8) {
+ return tud_printer_n_peek(0, ui8);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write(const void *buffer, uint32_t bufsize) {
+ return tud_printer_n_write(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_printer_write_flush(void) {
+ return tud_printer_n_write_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_printer_write_clear(void) {
+ return tud_printer_n_write_clear(0);
+}
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+void tud_printer_rx_cb(uint8_t itf);
+
+// Invoked when last write transfer is completed
+void tud_printer_tx_complete_cb(uint8_t itf);
+
+// Invoked when host requests device ID string (IEEE 1284).
+// Application returns pointer to device ID buffer (must remain valid until transfer completes).
+// First 2 bytes of returned buffer must contain big-endian length (including the 2 length bytes).
+const uint8_t *tud_printer_get_device_id_cb(uint8_t itf);
+
+// Invoked when host requests port status.
+uint8_t tud_printer_get_port_status_cb(uint8_t itf);
+
+// Invoked when host requests soft reset.
+void tud_printer_soft_reset_cb(uint8_t itf);
+
+// Invoked when a control request is completed (GET_DEVICE_ID, GET_PORT_STATUS, etc.)
+void tud_printer_request_complete_cb(uint8_t itf, tusb_control_request_t const *request);
+
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void printerd_init(void);
+bool printerd_deinit(void);
+void printerd_reset(uint8_t rhport);
+uint16_t printerd_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+bool printerd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request);
+bool printerd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index 327de087c..3bf5e1a17 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBTMC_H__
-#define _TUSB_USBTMC_H__
+#ifndef TUSB_USBTMC_H__
+#define TUSB_USBTMC_H__
#include "common/tusb_common.h"
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index 3f6bedd4c..4b0bb01ec 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -261,7 +261,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
bool stateChanged =
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
TU_VERIFY(stateChanged);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen, false));
return true;
}
@@ -273,7 +273,7 @@ bool tud_usbtmc_transmit_notification_data(const void *data, size_t len) {
TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
TU_VERIFY(tu_memcpy_s(usbtmc_epbuf.epnotif, CFG_TUD_USBTMC_INT_EP_SIZE, data, len) == 0);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t) len));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t) len, false));
return true;
}
@@ -396,7 +396,7 @@ bool tud_usbtmc_start_bus_read(void) {
default:
return false;
}
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_epbuf.epout, (uint16_t) usbtmc_state.ep_bulk_out_wMaxPacketSize));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_epbuf.epout, (uint16_t) usbtmc_state.ep_bulk_out_wMaxPacketSize, false));
return true;
}
@@ -567,7 +567,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
if (usbtmc_state.transfer_size_remaining >= USBTMCD_BUFFER_SIZE) {
// Copy buffer to ensure alignment correctness
memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, USBTMCD_BUFFER_SIZE);
- TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, USBTMCD_BUFFER_SIZE));
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, USBTMCD_BUFFER_SIZE, false));
usbtmc_state.devInBuffer += USBTMCD_BUFFER_SIZE;
usbtmc_state.transfer_size_remaining -= USBTMCD_BUFFER_SIZE;
usbtmc_state.transfer_size_sent += USBTMCD_BUFFER_SIZE;
@@ -578,7 +578,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
usbtmc_state.transfer_size_sent += packetLen;
usbtmc_state.transfer_size_remaining = 0;
usbtmc_state.devInBuffer = NULL;
- TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen));
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen, false));
if (((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0)) {
usbtmc_state.state = STATE_TX_SHORTED;
}
@@ -587,7 +587,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_ABORTING_BULK_IN:
// need to send short packet (ZLP?)
- TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u));
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u, false));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
return true;
@@ -713,7 +713,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
criticalLeave();
if (usbtmc_state.transfer_size_sent == 0) {
// Send short packet, nothing is in the buffer yet
- TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u));
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u, false));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
}
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
@@ -841,7 +841,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
},
.StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))};
// Must be queued before control request response sent (USB488v1.0 4.3.1.2)
- usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void *) &intMsg, sizeof(intMsg));
+ usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void *) &intMsg, sizeof(intMsg), false);
}
} else {
rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 27724b194..e1017ba48 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -37,120 +37,160 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
+ uint8_t rhport;
uint8_t itf_num;
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
/*------------- From this point, data is not cleared by bus reset -------------*/
- struct {
- tu_edpt_stream_t stream;
- #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
- uint8_t ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
- #endif
- } tx;
-
- struct {
- tu_edpt_stream_t stream;
- #if CFG_TUD_VENDOR_RX_BUFSIZE > 0
- uint8_t ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
- #endif
- } rx;
-
+ tu_edpt_stream_t tx_stream;
+ tu_edpt_stream_t rx_stream;
+ uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
+ uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
+ #else
+ uint8_t ep_in;
+ uint8_t ep_out;
+ uint16_t rx_xfer_len;
+ #endif
} vendord_interface_t;
-#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num))
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ #define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + TU_FIELD_SIZE(vendord_interface_t, itf_num))
+ #else
+ #define ITF_MEM_RESET_SIZE sizeof(vendord_interface_t)
+ #endif
static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
+// Skip local EP buffer if dedicated hw FIFO is supported or no fifo mode
+#if CFG_TUD_EDPT_DEDICATED_HWFIFO == 0 || !CFG_TUD_VENDOR_TXRX_BUFFERED
typedef struct {
- TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE);
- TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_RX_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_TX_EPSIZE);
} vendord_epbuf_t;
CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
+#endif
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
-TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) {
- (void) itf;
- (void) buffer;
- (void) bufsize;
+TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize) {
+ (void)idx;
+ (void)buffer;
+ (void)bufsize;
}
-TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) {
- (void) itf;
+TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes) {
+ (void)idx;
(void) sent_bytes;
}
-//--------------------------------------------------------------------
-// Application API
-//--------------------------------------------------------------------
+bool tud_vendor_n_mounted(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
-bool tud_vendor_n_mounted(uint8_t itf) {
- TU_VERIFY(itf < CFG_TUD_VENDOR);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- return p_itf->rx.stream.ep_addr || p_itf->tx.stream.ep_addr;
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ return (p_itf->rx_stream.ep_addr != 0) || (p_itf->tx_stream.ep_addr != 0);
+ #else
+ return (p_itf->ep_out != 0) || (p_itf->ep_in != 0);
+ #endif
}
//--------------------------------------------------------------------+
// Read API
//--------------------------------------------------------------------+
-uint32_t tud_vendor_n_available(uint8_t itf) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
-
- return tu_edpt_stream_read_available(&p_itf->rx.stream);
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+uint32_t tud_vendor_n_available(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ return tu_edpt_stream_read_available(&p_itf->rx_stream);
}
-bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) {
- TU_VERIFY(itf < CFG_TUD_VENDOR);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
-
- return tu_edpt_stream_peek(&p_itf->rx.stream, u8);
+bool tud_vendor_n_peek(uint8_t idx, uint8_t *u8) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ return tu_edpt_stream_peek(&p_itf->rx_stream, u8);
}
-uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- const uint8_t rhport = 0;
+uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ return tu_edpt_stream_read(&p_itf->rx_stream, buffer, bufsize);
+}
- return tu_edpt_stream_read(rhport, &p_itf->rx.stream, buffer, bufsize);
+void tud_vendor_n_read_flush(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, );
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ tu_edpt_stream_clear(&p_itf->rx_stream);
+ tu_edpt_stream_read_xfer(&p_itf->rx_stream);
}
+ #endif
+
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER
+bool tud_vendor_n_read_xfer(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
-void tud_vendor_n_read_flush (uint8_t itf) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, );
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- const uint8_t rhport = 0;
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ return tu_edpt_stream_read_xfer(&p_itf->rx_stream);
- tu_edpt_stream_clear(&p_itf->rx.stream);
- tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream);
+ #else
+ // Non-FIFO mode
+ TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_out));
+ return usbd_edpt_xfer(p_itf->rhport, p_itf->ep_out, _vendord_epbuf[idx].epout, p_itf->rx_xfer_len, false);
+ #endif
}
+ #endif
+
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
-uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- const uint8_t rhport = 0;
+uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ return tu_edpt_stream_write(&p_itf->tx_stream, buffer, (uint16_t)bufsize);
- return tu_edpt_stream_write(rhport, &p_itf->tx.stream, buffer, (uint16_t) bufsize);
+ #else
+ // non-fifo mode: direct transfer
+ TU_VERIFY(usbd_edpt_claim(p_itf->rhport, p_itf->ep_in), 0);
+ const uint32_t xact_len = tu_min32(bufsize, CFG_TUD_VENDOR_TX_EPSIZE);
+ memcpy(_vendord_epbuf[idx].epin, buffer, xact_len);
+ TU_ASSERT(usbd_edpt_xfer(p_itf->rhport, p_itf->ep_in, _vendord_epbuf[idx].epin, (uint16_t)xact_len, false), 0);
+ return xact_len;
+ #endif
}
-uint32_t tud_vendor_n_write_flush (uint8_t itf) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- const uint8_t rhport = 0;
+uint32_t tud_vendor_n_write_available(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ return tu_edpt_stream_write_available(&p_itf->tx_stream);
- return tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream);
+ #else
+ // Non-FIFO mode
+ TU_VERIFY(p_itf->ep_in > 0, 0); // must be opened
+ return usbd_edpt_busy(p_itf->rhport, p_itf->ep_in) ? 0 : CFG_TUD_VENDOR_TX_EPSIZE;
+ #endif
}
-uint32_t tud_vendor_n_write_available (uint8_t itf) {
- TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
- vendord_interface_t* p_itf = &_vendord_itf[itf];
- const uint8_t rhport = 0;
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+uint32_t tud_vendor_n_write_flush(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ return tu_edpt_stream_write_xfer(&p_itf->tx_stream);
+}
- return tu_edpt_stream_write_available(rhport, &p_itf->tx.stream);
+bool tud_vendor_n_write_clear(uint8_t idx) {
+ TU_VERIFY(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_itf = &_vendord_itf[idx];
+ tu_edpt_stream_clear(&p_itf->tx_stream);
+ return true;
}
+#endif
//--------------------------------------------------------------------+
// USBD Driver API
@@ -158,40 +198,35 @@ uint32_t tud_vendor_n_write_available (uint8_t itf) {
void vendord_init(void) {
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
- vendord_interface_t* p_itf = &_vendord_itf[i];
- vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i];
-
- uint8_t* rx_ff_buf =
- #if CFG_TUD_VENDOR_RX_BUFSIZE > 0
- p_itf->rx.ff_buf;
- #else
- NULL;
- #endif
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ for (uint8_t i = 0; i < CFG_TUD_VENDOR; i++) {
+ vendord_interface_t *p_itf = &_vendord_itf[i];
- tu_edpt_stream_init(&p_itf->rx.stream, false, false, false,
- rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE,
- p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE);
+ #if CFG_TUD_EDPT_DEDICATED_HWFIFO
+ uint8_t *epout_buf = NULL;
+ uint8_t *epin_buf = NULL;
+ #else
+ uint8_t *epout_buf = _vendord_epbuf[i].epout;
+ uint8_t *epin_buf = _vendord_epbuf[i].epin;
+ #endif
- uint8_t* tx_ff_buf =
- #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
- p_itf->tx.ff_buf;
- #else
- NULL;
- #endif
+ uint8_t *rx_ff_buf = p_itf->rx_ff_buf;
+ tu_edpt_stream_init(&p_itf->rx_stream, false, false, false, rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, epout_buf);
- tu_edpt_stream_init(&p_itf->tx.stream, false, true, false,
- tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE,
- p_epbuf->epin, CFG_TUD_VENDOR_EPSIZE);
+ uint8_t *tx_ff_buf = p_itf->tx_ff_buf;
+ tu_edpt_stream_init(&p_itf->tx_stream, false, true, false, tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, epin_buf);
}
+ #endif
}
bool vendord_deinit(void) {
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
- vendord_interface_t* p_itf = &_vendord_itf[i];
- tu_edpt_stream_deinit(&p_itf->rx.stream);
- tu_edpt_stream_deinit(&p_itf->tx.stream);
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ for (uint8_t i = 0; i < CFG_TUD_VENDOR; i++) {
+ vendord_interface_t *p_itf = &_vendord_itf[i];
+ tu_edpt_stream_deinit(&p_itf->rx_stream);
+ tu_edpt_stream_deinit(&p_itf->tx_stream);
}
+ #endif
return true;
}
@@ -201,30 +236,58 @@ void vendord_reset(uint8_t rhport) {
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
vendord_interface_t* p_itf = &_vendord_itf[i];
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
- tu_edpt_stream_clear(&p_itf->rx.stream);
- tu_edpt_stream_clear(&p_itf->tx.stream);
- tu_edpt_stream_close(&p_itf->rx.stream);
- tu_edpt_stream_close(&p_itf->tx.stream);
+
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ tu_edpt_stream_clear(&p_itf->rx_stream);
+ tu_edpt_stream_close(&p_itf->rx_stream);
+ tu_edpt_stream_clear(&p_itf->tx_stream);
+ tu_edpt_stream_close(&p_itf->tx_stream);
+ #endif
+ }
+}
+
+// Find vendor interface by endpoint address
+static uint8_t find_vendor_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_VENDOR; idx++) {
+ const vendord_interface_t *p_vendor = &_vendord_itf[idx];
+ if (ep_addr == 0) {
+ // find unused: require both ep == 0
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ if (p_vendor->rx_stream.ep_addr == 0 && p_vendor->tx_stream.ep_addr == 0) {
+ return idx;
+ }
+ #else
+ if (p_vendor->ep_out == 0 && p_vendor->ep_in == 0) {
+ return idx;
+ }
+ #endif
+ } else {
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ if (ep_addr == p_vendor->rx_stream.ep_addr || ep_addr == p_vendor->tx_stream.ep_addr) {
+ return idx;
+ }
+ #else
+ if (ep_addr == p_vendor->ep_out || ep_addr == p_vendor->ep_in) {
+ return idx;
+ }
+ #endif
+ }
}
+ return 0xff;
}
-uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
+uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
const uint8_t* p_desc = tu_desc_next(desc_itf);
// Find available interface
- vendord_interface_t* p_vendor = NULL;
- uint8_t itf;
- for(itf=0; itf<CFG_TUD_VENDOR; itf++) {
- if (!tud_vendor_n_mounted(itf)) {
- p_vendor = &_vendord_itf[itf];
- break;
- }
- }
- TU_VERIFY(p_vendor, 0);
-
+ const uint8_t idx = find_vendor_itf(0);
+ TU_ASSERT(idx < CFG_TUD_VENDOR, 0);
+ vendord_interface_t *p_vendor = &_vendord_itf[idx];
+ p_vendor->rhport = rhport;
p_vendor->itf_num = desc_itf->bInterfaceNumber;
+
while (tu_desc_in_bounds(p_desc, desc_end)) {
const uint8_t desc_type = tu_desc_type(p_desc);
if (desc_type == TUSB_DESC_INTERFACE || desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) {
@@ -233,61 +296,79 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
- // open endpoint stream, skip if already opened
+ uint16_t rx_xfer_len = CFG_TUD_VENDOR_RX_NEED_ZLP ? CFG_TUD_VENDOR_RX_EPSIZE : tu_edpt_packet_size(desc_ep);
+
+ #if CFG_TUD_VENDOR_TXRX_BUFFERED
+ // open endpoint stream
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- if (p_vendor->tx.stream.ep_addr == 0) {
- tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
- tud_vendor_n_write_flush(itf);
- }
+ tu_edpt_stream_t *tx_stream = &p_vendor->tx_stream;
+ tu_edpt_stream_open(tx_stream, rhport, desc_ep, CFG_TUD_VENDOR_TX_EPSIZE);
+ tu_edpt_stream_write_xfer(tx_stream); // flush pending data
} else {
- if (p_vendor->rx.stream.ep_addr == 0) {
- tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
- TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
- }
+ tu_edpt_stream_t *rx_stream = &p_vendor->rx_stream;
+ tu_edpt_stream_open(rx_stream, rhport, desc_ep, rx_xfer_len);
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
+ TU_ASSERT(tu_edpt_stream_read_xfer(rx_stream) > 0, 0); // prepare for incoming data
+ #endif
}
+ #else
+ p_vendor->rx_xfer_len = rx_xfer_len;
+ // Non-FIFO mode: store endpoint info
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ p_vendor->ep_in = desc_ep->bEndpointAddress;
+ } else {
+ p_vendor->ep_out = desc_ep->bEndpointAddress;
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
+ // Prepare for incoming data
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, rx_xfer_len, false), 0);
+ #endif
+ }
+ #endif
}
p_desc = tu_desc_next(p_desc);
}
- return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf);
+ return (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_itf);
}
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
- (void) result;
-
- uint8_t itf;
- vendord_interface_t* p_vendor;
-
- for (itf = 0; itf < CFG_TUD_VENDOR; itf++) {
- p_vendor = &_vendord_itf[itf];
- if ((ep_addr == p_vendor->rx.stream.ep_addr) || (ep_addr == p_vendor->tx.stream.ep_addr)) {
- break;
- }
- }
- TU_VERIFY(itf < CFG_TUD_VENDOR);
- vendord_epbuf_t* p_epbuf = &_vendord_epbuf[itf];
-
- if ( ep_addr == p_vendor->rx.stream.ep_addr ) {
- // Received new data: put into stream's fifo
- tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes);
-
- // Invoked callback if any
- tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes);
+ (void)rhport;
+ (void)result;
+ const uint8_t idx = find_vendor_itf(ep_addr);
+ TU_VERIFY(idx < CFG_TUD_VENDOR);
+ vendord_interface_t *p_vendor = &_vendord_itf[idx];
- tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream);
- } else if ( ep_addr == p_vendor->tx.stream.ep_addr ) {
+#if CFG_TUD_VENDOR_TXRX_BUFFERED
+ if (ep_addr == p_vendor->rx_stream.ep_addr) {
+ // Put received data to FIFO
+ tu_edpt_stream_read_xfer_complete(&p_vendor->rx_stream, xferred_bytes);
+ tud_vendor_rx_cb(idx, NULL, 0);
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
+ tu_edpt_stream_read_xfer(&p_vendor->rx_stream); // prepare next data
+ #endif
+ } else if (ep_addr == p_vendor->tx_stream.ep_addr) {
// Send complete
- tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
+ tud_vendor_tx_cb(idx, (uint16_t)xferred_bytes);
- #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
// try to send more if possible
- if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->tx.stream) ) {
+ if (0 == tu_edpt_stream_write_xfer(&p_vendor->tx_stream)) {
// If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero
- tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->tx.stream, xferred_bytes);
+ tu_edpt_stream_write_zlp_if_needed(&p_vendor->tx_stream, xferred_bytes);
}
+ }
+ #else
+ if (ep_addr == p_vendor->ep_out) {
+ // Non-FIFO mode: invoke callback with buffer
+ tud_vendor_rx_cb(idx, _vendord_epbuf[idx].epout, xferred_bytes);
+ #if CFG_TUD_VENDOR_RX_MANUAL_XFER == 0
+ usbd_edpt_xfer(rhport, p_vendor->ep_out, _vendord_epbuf[idx].epout, p_vendor->rx_xfer_len, false);
#endif
+ } else if (ep_addr == p_vendor->ep_in) {
+ // Send complete
+ tud_vendor_tx_cb(idx, (uint16_t)xferred_bytes);
}
+ #endif
return true;
}
diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h
index 5fe4fc9ff..cdca9fd15 100644
--- a/src/class/vendor/vendor_device.h
+++ b/src/class/vendor/vendor_device.h
@@ -24,92 +24,161 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_VENDOR_DEVICE_H_
-#define _TUSB_VENDOR_DEVICE_H_
+#ifndef TUSB_VENDOR_DEVICE_H_
+#define TUSB_VENDOR_DEVICE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
#include "common/tusb_common.h"
-#ifndef CFG_TUD_VENDOR_EPSIZE
-#define CFG_TUD_VENDOR_EPSIZE 64
+//--------------------------------------------------------------------+
+// Configuration
+//--------------------------------------------------------------------+
+#ifndef CFG_TUD_VENDOR_RX_EPSIZE
+ #ifdef CFG_TUD_VENDOR_EPSIZE
+ #define CFG_TUD_VENDOR_RX_EPSIZE CFG_TUD_VENDOR_EPSIZE
+ #else
+ #define CFG_TUD_VENDOR_RX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
+#endif
+
+#ifndef CFG_TUD_VENDOR_TX_EPSIZE
+ #ifdef CFG_TUD_VENDOR_EPSIZE
+ #define CFG_TUD_VENDOR_TX_EPSIZE CFG_TUD_VENDOR_EPSIZE
+ #else
+ #define CFG_TUD_VENDOR_TX_EPSIZE TUD_EPSIZE_BULK_MAX
+ #endif
#endif
// RX FIFO can be disabled by setting this value to 0
#ifndef CFG_TUD_VENDOR_RX_BUFSIZE
-#define CFG_TUD_VENDOR_RX_BUFSIZE 64
+ #define CFG_TUD_VENDOR_RX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
// TX FIFO can be disabled by setting this value to 0
#ifndef CFG_TUD_VENDOR_TX_BUFSIZE
-#define CFG_TUD_VENDOR_TX_BUFSIZE 64
+ #define CFG_TUD_VENDOR_TX_BUFSIZE TUD_EPSIZE_BULK_MAX
#endif
-#ifdef __cplusplus
- extern "C" {
+// Vendor is buffered (FIFO mode) if both TX and RX buffers are configured
+// If either is 0, vendor operates in non-buffered (direct transfer) mode
+#ifndef CFG_TUD_VENDOR_TXRX_BUFFERED
+ #define CFG_TUD_VENDOR_TXRX_BUFFERED ((CFG_TUD_VENDOR_RX_BUFSIZE > 0) && (CFG_TUD_VENDOR_TX_BUFSIZE > 0))
+#endif
+
+// Application will manually schedule RX transfer. This can be useful when using with non-fifo (buffered) mode
+// i.e. CFG_TUD_VENDOR_TXRX_BUFFERED = 0
+#ifndef CFG_TUD_VENDOR_RX_MANUAL_XFER
+ #define CFG_TUD_VENDOR_RX_MANUAL_XFER 0
+#endif
+
+// Enable multi-packet RX transfer with ZLP termination for better throughput. Requires host support for ZLP.
+#ifndef CFG_TUD_VENDOR_RX_NEED_ZLP
+ #define CFG_TUD_VENDOR_RX_NEED_ZLP 0
#endif
//--------------------------------------------------------------------+
// Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1
//--------------------------------------------------------------------+
-bool tud_vendor_n_mounted (uint8_t itf);
-uint32_t tud_vendor_n_available (uint8_t itf);
-uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
-bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
-void tud_vendor_n_read_flush (uint8_t itf);
-uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
-uint32_t tud_vendor_n_write_flush (uint8_t itf);
-uint32_t tud_vendor_n_write_available (uint8_t itf);
+// Return whether the vendor interface is mounted
+bool tud_vendor_n_mounted(uint8_t idx);
+
+//------------- RX -------------//
+#if CFG_TUD_VENDOR_TXRX_BUFFERED
+// Return number of available bytes for reading
+uint32_t tud_vendor_n_available(uint8_t idx);
+
+// Peek a byte from RX buffer
+bool tud_vendor_n_peek(uint8_t idx, uint8_t *ui8);
+
+// Read from RX FIFO
+uint32_t tud_vendor_n_read(uint8_t idx, void *buffer, uint32_t bufsize);
+
+// Flush (clear) RX FIFO
+void tud_vendor_n_read_flush(uint8_t idx);
+#endif
+
+#if CFG_TUD_VENDOR_RX_MANUAL_XFER
+// Start a new RX transfer to fill the RX FIFO, return false if previous transfer is still ongoing
+bool tud_vendor_n_read_xfer(uint8_t idx);
+#endif
+
+//------------- TX -------------//
+// Write to TX FIFO. This can be buffered and not sent immediately unless buffered bytes >= USB endpoint size
+uint32_t tud_vendor_n_write(uint8_t idx, const void *buffer, uint32_t bufsize);
+
+// Return number of bytes available for writing in TX FIFO (or endpoint if non-buffered)
+uint32_t tud_vendor_n_write_available(uint8_t idx);
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
+#if CFG_TUD_VENDOR_TXRX_BUFFERED
+// Force sending buffered data, return number of bytes sent
+uint32_t tud_vendor_n_write_flush(uint8_t idx);
+
+// Clear the transmit FIFO
+bool tud_vendor_n_write_clear(uint8_t idx);
+#endif
+
+// Write a null-terminated string to TX FIFO
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t idx, const char *str) {
+ return tud_vendor_n_write(idx, str, strlen(str));
+}
// backward compatible
-#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf)
+#define tud_vendor_n_flush(idx) tud_vendor_n_write_flush(idx)
//--------------------------------------------------------------------+
// Application API (Single Port) i.e CFG_TUD_VENDOR = 1
//--------------------------------------------------------------------+
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t itf, char const* str) {
- return tud_vendor_n_write(itf, str, strlen(str));
-}
-
TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_mounted(void) {
- return tud_vendor_n_mounted(0);
+ return tud_vendor_n_mounted(0);
}
+#if CFG_TUD_VENDOR_TXRX_BUFFERED
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_available(void) {
- return tud_vendor_n_available(0);
+ return tud_vendor_n_available(0);
}
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) {
- return tud_vendor_n_read(0, buffer, bufsize);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t *ui8) {
+ return tud_vendor_n_peek(0, ui8);
}
-TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t* ui8) {
- return tud_vendor_n_peek(0, ui8);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void *buffer, uint32_t bufsize) {
+ return tud_vendor_n_read(0, buffer, bufsize);
}
TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) {
- tud_vendor_n_read_flush(0);
+ tud_vendor_n_read_flush(0);
}
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(void const* buffer, uint32_t bufsize) {
- return tud_vendor_n_write(0, buffer, bufsize);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) {
+ return tud_vendor_n_write_flush(0);
}
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(char const* str) {
- return tud_vendor_n_write_str(0, str);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_write_clear(void) {
+ return tud_vendor_n_write_clear(0);
}
+#endif
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) {
- return tud_vendor_n_write_flush(0);
+#if CFG_TUD_VENDOR_RX_MANUAL_XFER
+TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_read_xfer(void) {
+ return tud_vendor_n_read_xfer(0);
+}
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(const void *buffer, uint32_t bufsize) {
+ return tud_vendor_n_write(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(const char *str) {
+ return tud_vendor_n_write_str(0, str);
}
-#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
- return tud_vendor_n_write_available(0);
+ return tud_vendor_n_write_available(0);
}
-#endif
// backward compatible
#define tud_vendor_flush() tud_vendor_write_flush()
@@ -118,15 +187,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
-// Invoked when received new data
-void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
-// Invoked when last rx transfer finished
-void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
+// Invoked when received new data.
+// - CFG_TUD_VENDOR_TXRX_BUFFERED = 1: buffer and bufsize must not be used (both NULL,0) since data is in RX FIFO
+// - CFG_TUD_VENDOR_TXRX_BUFFERED = 0: Buffer and bufsize are valid
+void tud_vendor_rx_cb(uint8_t idx, const uint8_t *buffer, uint32_t bufsize);
+// Invoked when tx transfer is finished
+void tud_vendor_tx_cb(uint8_t idx, uint32_t sent_bytes);
//--------------------------------------------------------------------+
// Internal Class Driver API
@@ -134,11 +201,11 @@ void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
void vendord_init(void);
bool vendord_deinit(void);
void vendord_reset(uint8_t rhport);
-uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t *idx_desc, uint16_t max_len);
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_VENDOR_DEVICE_H_ */
+#endif /* TUSB_VENDOR_DEVICE_H_ */
diff --git a/src/class/vendor/vendor_host.h b/src/class/vendor/vendor_host.h
index acfebe7a4..00e3c3402 100644
--- a/src/class/vendor/vendor_host.h
+++ b/src/class/vendor/vendor_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_VENDOR_HOST_H_
-#define _TUSB_VENDOR_HOST_H_
+#ifndef TUSB_VENDOR_HOST_H_
+#define TUSB_VENDOR_HOST_H_
#include "common/tusb_common.h"
@@ -64,4 +64,4 @@ void cush_close(uint8_t dev_addr);
}
#endif
-#endif /* _TUSB_VENDOR_HOST_H_ */
+#endif /* TUSB_VENDOR_HOST_H_ */
diff --git a/src/class/video/video.h b/src/class/video/video.h
index f348e187b..5bdf4b840 100644
--- a/src/class/video/video.h
+++ b/src/class/video/video.h
@@ -219,11 +219,11 @@ typedef enum {
uint8_t baInterfaceNr[_nitf]; \
}
-typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
-typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
-typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
-typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
-typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
+typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t; //-V2586 incorrectly detected as flexible array
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 5c00cc358..e31ab4194 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -70,6 +70,13 @@ typedef struct TU_ATTR_PACKED {
uint8_t bEntityId;
} tusb_desc_cs_video_entity_itf_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t wMaxTransferSize;
+} tusb_desc_cs_video_vc_ep_t;
+
typedef union {
struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -103,7 +110,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t index_vc; /* index of bound video control interface */
uint8_t index_vs; /* index from the video control interface */
struct {
- uint16_t beg; /* Offset of the begging of video streaming interface descriptor */
+ uint16_t beg; /* Offset of the beginning of video streaming interface descriptor */
uint16_t end; /* Offset of the end of video streaming interface descriptor */
uint16_t cur; /* Offset of the current settings */
uint16_t ep[2]; /* Offset of endpoint descriptors. 0: streaming, 1: still capture */
@@ -214,6 +221,12 @@ TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
return VIDEO_ERROR_NONE;
}
+TU_ATTR_WEAK void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request) {
+ (void) ctl_idx;
+ (void) stm_idx;
+ (void) request;
+}
+
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -244,9 +257,13 @@ static inline uint8_t _desc_ep_addr(void const *desc) {
* @return instance */
static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
videod_interface_t *ctl = &_videod_itf[ctl_idx];
- if (!ctl->beg) return NULL;
+ if (!ctl->beg) {
+ return NULL;
+ }
videod_streaming_interface_t *stm = &_videod_streaming_itf[ctl->stm[stm_idx]];
- if (!stm->desc.beg) return NULL;
+ if (!stm->desc.beg) {
+ return NULL;
+ }
return stm;
}
@@ -255,7 +272,9 @@ static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self) {
}
static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self) {
- if (!self->desc.cur) return NULL;
+ if (!self->desc.cur) {
+ return NULL;
+ }
uint8_t const *desc = _videod_itf[self->index_vc].beg;
return (tusb_desc_vs_itf_t const*)(desc + self->desc.cur);
}
@@ -366,8 +385,12 @@ static void const* _find_desc_ep(void const *beg, void const *end)
{
for (void const *cur = beg; cur < end; cur = tu_desc_next(cur)) {
uint_fast8_t desc_type = tu_desc_type(cur);
- if (TUSB_DESC_ENDPOINT == desc_type) return cur;
- if (TUSB_DESC_INTERFACE == desc_type) break;
+ if (TUSB_DESC_ENDPOINT == desc_type) {
+ return cur;
+ }
+ if (TUSB_DESC_INTERFACE == desc_type) {
+ break;
+ }
}
return end;
}
@@ -453,8 +476,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
uint_fast8_t fmtnum = param->bFormatIndex;
TU_ASSERT(vs && fmtnum <= vs->stm.bNumFormats);
- if (!fmtnum) {
- if (1 < vs->stm.bNumFormats) return true; /* Need to negotiate all variables. */
+ if (0 == fmtnum) {
+ if (1 < vs->stm.bNumFormats) {
+ return true; /* Need to negotiate all variables. */
+ }
fmtnum = 1;
param->bFormatIndex = 1;
}
@@ -492,8 +517,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
uint_fast8_t frmnum = param->bFrameIndex;
TU_ASSERT(frmnum <= fmt->bNumFrameDescriptors);
- if (!frmnum) {
- if (1 < fmt->bNumFrameDescriptors) return true;
+ if (0 == frmnum) {
+ if (1 < fmt->bNumFrameDescriptors) {
+ return true;
+ }
frmnum = 1;
param->bFrameIndex = 1;
}
@@ -502,7 +529,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
/* Set the parameters determined by the frame */
uint_fast32_t frame_size = param->dwMaxVideoFrameSize;
- if (!frame_size) {
+ if (0 == frame_size) {
switch (fmt->bDescriptorSubType) {
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8;
@@ -522,7 +549,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
}
uint_fast32_t interval = param->dwFrameInterval;
- if (!interval) {
+ if (0 == interval) {
if ((1 < frm->uncompressed.bFrameIntervalType) ||
((0 == frm->uncompressed.bFrameIntervalType) &&
(frm->uncompressed.dwFrameInterval[1] != frm->uncompressed.dwFrameInterval[0]))) {
@@ -532,7 +559,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
param->dwFrameInterval = interval;
}
uint_fast32_t interval_ms = interval / 10000;
- TU_ASSERT(interval_ms);
+ TU_ASSERT(interval_ms != 0);
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
@@ -550,7 +577,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
video_probe_and_commit_control_t *param)
{
uint_fast8_t const fmtnum = param->bFormatIndex;
- if (!fmtnum) {
+ if (0 == fmtnum) {
switch (request) {
case VIDEO_REQUEST_GET_MAX:
if (_get_desc_vs(stm))
@@ -581,7 +608,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
}
uint_fast8_t frmnum = param->bFrameIndex;
- if (!frmnum) {
+ if (0 == frmnum) {
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
TU_ASSERT(vs);
void const *end = _end_of_streaming_descriptor(vs);
@@ -637,7 +664,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
return true;
}
- if (!param->dwFrameInterval) {
+ if (0 == param->dwFrameInterval) {
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
TU_ASSERT(vs);
void const *end = _end_of_streaming_descriptor(vs);
@@ -686,12 +713,12 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
default: return false;
}
param->dwFrameInterval = interval;
- if (!interval) {
+ if (0 == interval) {
param->dwMaxPayloadTransferSize = 0;
} else {
uint_fast32_t frame_size = param->dwMaxVideoFrameSize;
uint_fast32_t payload_size;
- if (!interval_ms) {
+ if (0 == interval_ms) {
payload_size = frame_size + 2;
} else {
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
@@ -719,7 +746,10 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self)
/* The end of the video control interface descriptor. */
void const *end = _end_of_control_descriptor(vc);
- if (vc->std.bNumEndpoints) {
+ if (vc->std.bNumEndpoints != 0) {
+ /* Extend end to cover the standard endpoint and class-specific endpoint descriptors
+ * that follow wTotalLength */
+ end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t);
/* Find the notification endpoint descriptor. */
cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT);
TU_ASSERT(cur < end);
@@ -757,9 +787,12 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
cur += vc->std.bLength + vc->ctl.bLength;
TU_LOG_DRV(" bNumEndpoints %d\r\n", vc->std.bNumEndpoints);
/* Open the notification endpoint if it exist. */
- if (vc->std.bNumEndpoints) {
+ if (vc->std.bNumEndpoints != 0) {
/* Support for 1 endpoint only. */
TU_VERIFY(1 == vc->std.bNumEndpoints);
+ /* Extend end to cover the standard endpoint and class-specific endpoint descriptors
+ * that follow wTotalLength */
+ end = (uint8_t const*)end + sizeof(tusb_desc_endpoint_t) + sizeof(tusb_desc_cs_video_vc_ep_t);
/* Find the notification endpoint descriptor. */
cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT);
TU_VERIFY(cur < end);
@@ -832,7 +865,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
/* FS must be less than or equal to max packet size */
TU_VERIFY (tu_edpt_packet_size(ep) >= max_size);
#ifdef TUP_DCD_EDPT_ISO_ALLOC
- usbd_edpt_iso_activate(rhport, ep);
+ TU_ASSERT(usbd_edpt_iso_activate(rhport, ep));
#else
TU_ASSERT(usbd_edpt_open(rhport, ep));
#endif
@@ -843,7 +876,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
stm->desc.ep[i] = (uint16_t) (cur - desc);
TU_LOG_DRV(" open EP%02x\r\n", _desc_ep_addr(cur));
}
- if (altnum) {
+ if (altnum != 0) {
stm->state = VS_STATE_STREAMING;
}
TU_LOG_DRV(" done\r\n");
@@ -860,7 +893,16 @@ static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm, uint
}
TU_ASSERT(pkt_len >= hdr_len);
uint_fast16_t data_len = pkt_len - hdr_len;
- memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ if (stm->buffer) {
+ memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ } else {
+ tud_video_payload_request_t rqst = {
+ .buf = &ep_buf[hdr_len],
+ .length = data_len,
+ .offset = stm->offset
+ };
+ tud_video_prepare_payload_cb(stm->index_vc, stm->index_vs, &rqst);
+ }
stm->offset += data_len;
remaining -= data_len;
if (!remaining) {
@@ -929,16 +971,14 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN);
}
@@ -951,15 +991,13 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_VC_CTL_REQUEST_ERROR_CODE:
switch (request->bRequest) {
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(tud_control_xfer(rhport, request, &self->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get, sizeof(_cap_get)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -986,7 +1024,7 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
case TUSB_REQ_TYPE_CLASS: {
uint_fast8_t entity_id = TU_U16_HIGH(request->wIndex);
- if (!entity_id) {
+ if (0 == entity_id) {
return handle_video_ctl_cs_req(rhport, stage, request, ctl_idx);
} else {
TU_VERIFY(_find_desc_entity(_get_desc_vc(&_videod_itf[ctl_idx]), entity_id), VIDEO_ERROR_INVALID_REQUEST);
@@ -1001,14 +1039,12 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
- uint_fast8_t stm_idx)
-{
+ uint_fast8_t stm_idx) {
TU_LOG_DRV("\r\n");
videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
switch (request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
tusb_desc_vs_itf_t const *vs = _get_desc_vs(self);
TU_VERIFY(vs, VIDEO_ERROR_UNKNOWN);
@@ -1075,12 +1111,14 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
} else if (stage == CONTROL_STAGE_DATA) {
TU_VERIFY(_update_streaming_parameters(stm, &stm->probe_commit_payload),
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ } else {
+ // nothing to do
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1090,7 +1128,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_RES:
case VIDEO_REQUEST_GET_DEF:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
video_probe_and_commit_control_t tmp = stm->probe_commit_payload;
TU_VERIFY(_negotiate_streaming_parameters(stm, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN);
@@ -1137,12 +1175,14 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
hdr->bHeaderLength = sizeof(*hdr);
hdr->bmHeaderInfo = 0;
}
+ } else {
+ // nothing to do
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1185,14 +1225,15 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
static int handle_video_stm_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
- uint_fast8_t stm_idx)
-{
+ uint_fast8_t stm_idx) {
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
return handle_video_stm_std_req(rhport, stage, request, stm_idx);
case TUSB_REQ_TYPE_CLASS:
- if (TU_U16_HIGH(request->wIndex)) return VIDEO_ERROR_INVALID_REQUEST;
+ if (0 != TU_U16_HIGH(request->wIndex)) {
+ return VIDEO_ERROR_INVALID_REQUEST;
+ }
return handle_video_stm_cs_req(rhport, stage, request, stm_idx);
default: return VIDEO_ERROR_INVALID_REQUEST;
@@ -1203,11 +1244,12 @@ static int handle_video_stm_req(uint8_t rhport, uint8_t stage,
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_video_n_connected(uint_fast8_t ctl_idx)
-{
+bool tud_video_n_connected(uint_fast8_t ctl_idx) {
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, 0);
- if (stm) return true;
+ if (stm != NULL) {
+ return true;
+ }
return false;
}
@@ -1216,15 +1258,21 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
- if (!stm || !stm->desc.ep[0]) return false;
- if (stm->state == VS_STATE_PROBING) return false;
+ if (NULL == stm || 0 == stm->desc.ep[0]) {
+ return false;
+ }
+ if (stm->state == VS_STATE_PROBING) {
+ return false;
+ }
-#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ #ifdef TUP_DCD_EDPT_ISO_ALLOC
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
uint_fast16_t ofs_ep = stm->desc.ep[0];
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- if (stm->state == VS_STATE_COMMITTED) return false;
+ if (stm->state == VS_STATE_COMMITTED) {
+ return false;
+ }
}
#endif
@@ -1235,25 +1283,36 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
- if (!buffer || !bufsize) return false;
+ if (0 == bufsize) {
+ return false;
+ }
+
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[ctl_idx];
- if (!stm || !stm->desc.ep[0] || stm->buffer) return false;
- if (stm->state == VS_STATE_PROBING) return false;
+ if (NULL == stm || 0 == stm->desc.ep[0] || stm->bufsize) {
+ return false;
+ }
+ if (stm->state == VS_STATE_PROBING) {
+ return false;
+ }
/* Find EP address */
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
uint8_t ep_addr = 0;
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
uint_fast16_t ofs_ep = stm->desc.ep[i];
- if (!ofs_ep) continue;
+ if (0 == ofs_ep) {
+ continue;
+ }
ep_addr = _desc_ep_addr(desc + ofs_ep);
break;
}
- if (!ep_addr) return false;
+ if (0 == ep_addr) {
+ return false;
+ }
- TU_VERIFY( usbd_edpt_claim(0, ep_addr) );
+ TU_VERIFY(usbd_edpt_claim(0, ep_addr));
/* update the packet header */
tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
hdr->FrameID ^= 1;
@@ -1262,7 +1321,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
stm->buffer = (uint8_t*)buffer;
stm->bufsize = bufsize;
uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
- TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
+ TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len, false), 0);
return true;
}
@@ -1305,7 +1364,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
videod_interface_t *self = NULL;
uint8_t ctl_idx;
for (ctl_idx = 0; ctl_idx < CFG_TUD_VIDEO; ++ctl_idx) {
- if (_videod_itf[ctl_idx].beg) continue;
+ if (NULL != _videod_itf[ctl_idx].beg) {
+ continue;
+ }
self = &_videod_itf[ctl_idx];
break;
}
@@ -1326,7 +1387,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
videod_streaming_interface_t *stm = NULL;
/* find free streaming interface handle */
for (uint8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
- if (_videod_streaming_itf[i].desc.beg) continue;
+ if (0 != _videod_streaming_itf[i].desc.beg) {
+ continue;
+ }
stm = &_videod_streaming_itf[i];
self->stm[stm_idx] = i;
break;
@@ -1354,7 +1417,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
}
p_desc = tu_desc_next(p_desc);
}
- if(ep_addr > 0 && ep_size > 0) usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
+ if(ep_addr > 0 && ep_size > 0) {
+ usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
+ }
#endif
if (0 == stm_idx && 1 == bInCollection) {
/* If there is only one streaming interface and no alternate settings,
@@ -1381,31 +1446,43 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
uint_fast8_t itf;
for (itf = 0; itf < CFG_TUD_VIDEO; ++itf) {
void const *desc = _videod_itf[itf].beg;
- if (!desc) continue;
- if (itfnum == _desc_itfnum(desc)) break;
+ if (!desc) {
+ continue;
+ }
+ if (itfnum == _desc_itfnum(desc)) {
+ break;
+ }
}
if (itf < CFG_TUD_VIDEO) {
TU_LOG_DRV(" VC[%d]: ", itf);
err = handle_video_ctl_req(rhport, stage, request, itf);
_videod_itf[itf].error_code = (uint8_t)err;
- if (err) return false;
+ if (0 != err) {
+ return false;
+ }
return true;
}
/* Identify which streaming interface to use */
for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[itf];
- if (!stm->desc.beg) continue;
+ if (0 == stm->desc.beg) {
+ continue;
+ }
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
- if (itfnum == _desc_itfnum(desc + stm->desc.beg)) break;
+ if (itfnum == _desc_itfnum(desc + stm->desc.beg)) {
+ break;
+ }
}
if (itf < CFG_TUD_VIDEO_STREAMING) {
TU_LOG_DRV(" VS[%d]: ", itf);
err = handle_video_stm_req(rhport, stage, request, itf);
_videod_streaming_itf[itf].error_code = (uint8_t)err;
- if (err) return false;
+ if (err != 0) {
+ return false;
+ }
return true;
}
return false;
@@ -1421,19 +1498,23 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) {
stm = &_videod_streaming_itf[itf];
uint_fast16_t const ep_ofs = stm->desc.ep[0];
- if (!ep_ofs) continue;
+ if (0 == ep_ofs) {
+ continue;
+ }
ctl = &_videod_itf[stm->index_vc];
uint8_t const *desc = ctl->beg;
- if (ep_addr == _desc_ep_addr(desc + ep_ofs)) break;
+ if (ep_addr == _desc_ep_addr(desc + ep_ofs)) {
+ break;
+ }
}
TU_ASSERT(itf < CFG_TUD_VIDEO_STREAMING);
videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[itf];
if (stm->offset < stm->bufsize) {
/* Claim the endpoint */
- TU_VERIFY( usbd_edpt_claim(rhport, ep_addr), 0);
+ TU_VERIFY(usbd_edpt_claim(rhport, ep_addr), 0);
uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
- TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len, false), 0);
} else {
stm->buffer = NULL;
stm->bufsize = 0;
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index 2b41c3bfe..2750bb2fb 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -35,6 +35,16 @@
extern "C" {
#endif
+
+//--------------------------------------------------------------------+
+// Payload request
+//--------------------------------------------------------------------+
+typedef struct TU_ATTR_PACKED {
+ void* buf; /* Payload buffer to be filled */
+ size_t length; /* Length of the requested data in bytes */
+ size_t offset; /* Offset within the frame (in bytes) */
+} tud_video_payload_request_t;
+
//--------------------------------------------------------------------+
// Application API (Multiple Ports)
// CFG_TUD_VIDEO > 1
@@ -83,6 +93,15 @@ int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
video_probe_and_commit_control_t const *parameters);
+/** Invoked if buffer is set to NULL (allows bufferless on the fly data generation)
+ *
+ * @param[in] ctl_idx Destination control interface index
+ * @param[in] stm_idx Destination streaming interface index
+ * @param[out] payload_buf Payload storage buffer (target buffer for requested data)
+ * @param[in] payload_size Size of payload_buf (requested data size)
+ * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize) */
+void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request);
+
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+