summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authornxf58843 <[email protected]>2021-08-23 09:55:04 -0700
committerGitHub <[email protected]>2021-08-23 09:55:04 -0700
commitfb16e80e575a44828f5367f4fb7380162b0354f0 (patch)
treebb965cc935083d99e083667d4f0f1533d50a29f5 /src/class
parent616562a48aa1d8ce2f4ca71f6e780a8bec9fb5eb (diff)
parentea902493db49843dcdeca6bfa2b2efe540f44b2f (diff)
Merge pull request #2 from hathach/master
Pulling latest from source
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio.h899
-rw-r--r--src/class/audio/audio_device.c2272
-rw-r--r--src/class/audio/audio_device.h627
-rwxr-xr-xsrc/class/bth/bth_device.c254
-rwxr-xr-xsrc/class/bth/bth_device.h109
-rw-r--r--src/class/cdc/cdc.h31
-rw-r--r--src/class/cdc/cdc_device.c323
-rw-r--r--src/class/cdc/cdc_device.h89
-rw-r--r--src/class/cdc/cdc_host.c132
-rw-r--r--src/class/cdc/cdc_host.h23
-rw-r--r--src/class/cdc/cdc_rndis_host.c2
-rw-r--r--src/class/dfu/dfu.h119
-rw-r--r--src/class/dfu/dfu_device.c458
-rw-r--r--src/class/dfu/dfu_device.h98
-rw-r--r--src/class/dfu/dfu_rt_device.c94
-rw-r--r--src/class/dfu/dfu_rt_device.h37
-rw-r--r--src/class/hid/hid.h574
-rw-r--r--src/class/hid/hid_device.c321
-rw-r--r--src/class/hid/hid_device.h180
-rw-r--r--src/class/hid/hid_host.c655
-rw-r--r--src/class/hid/hid_host.h213
-rw-r--r--src/class/midi/midi.h45
-rw-r--r--src/class/midi/midi_device.c497
-rw-r--r--src/class/midi/midi_device.h115
-rw-r--r--src/class/msc/msc.h12
-rw-r--r--src/class/msc/msc_device.c477
-rw-r--r--src/class/msc/msc_device.h96
-rw-r--r--src/class/msc/msc_host.c569
-rw-r--r--src/class/msc/msc_host.h203
-rw-r--r--src/class/net/net_device.c441
-rw-r--r--src/class/net/net_device.h85
-rw-r--r--src/class/usbtmc/usbtmc_device.c79
-rw-r--r--src/class/usbtmc/usbtmc_device.h13
-rw-r--r--src/class/vendor/vendor_device.c34
-rw-r--r--src/class/vendor/vendor_device.h17
-rw-r--r--src/class/vendor/vendor_host.c8
-rw-r--r--src/class/vendor/vendor_host.h7
37 files changed, 8269 insertions, 1939 deletions
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 5bec14d88..f99061eae 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,28 +36,44 @@
#include "common/tusb_common.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-/// Audio Interface Subclass Codes
+/// Audio Device Class Codes
+
+/// A.2 - Audio Function Subclass Codes
+typedef enum
+{
+ AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
+} audio_function_subclass_type_t;
+
+/// A.3 - Audio Function Protocol Codes
+typedef enum
+{
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_function_protocol_code_t;
+
+/// A.5 - Audio Interface Subclass Codes
typedef enum
{
- AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control
+ AUDIO_SUBCLASS_UNDEFINED = 0x00,
+ AUDIO_SUBCLASS_CONTROL , ///< Audio Control
AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
} audio_subclass_type_t;
-/// Audio Protocol Codes
+/// A.6 - Audio Interface Protocol Codes
typedef enum
{
- AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0
- AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0
- AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0
-} audio_protocol_type_t;
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_interface_protocol_code_t;
-/// Audio Function Category Codes
+/// A.7 - Audio Function Category Codes
typedef enum
{
+ AUDIO_FUNC_UNDEF = 0x00,
AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
AUDIO_FUNC_HOME_THEATER = 0x02,
AUDIO_FUNC_MICROPHONE = 0x03,
@@ -68,31 +85,859 @@ typedef enum
AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
AUDIO_FUNC_PRO_AUDIO = 0x0A,
AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
- AUDIO_FUNC_CONTROL_PANEL = 0x0C
-} audio_function_t;
+ AUDIO_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO_FUNC_OTHER = 0xFF,
+} audio_function_code_t;
+
+/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio_cs_ac_interface_subtype_t;
+
+/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
+} audio_cs_as_interface_subtype_t;
+
+/// A.11 - Effect Unit Effect Types
+typedef enum
+{
+ AUDIO_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio_effect_unit_effect_type_t;
+
+/// A.12 - Processing Unit Process Types
+typedef enum
+{
+ AUDIO_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio_processing_unit_process_type_t;
+
+/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio_cs_ep_subtype_t;
+
+/// A.14 - Audio Class-Specific Request Codes
+typedef enum
+{
+ AUDIO_CS_REQ_UNDEF = 0x00,
+ AUDIO_CS_REQ_CUR = 0x01,
+ AUDIO_CS_REQ_RANGE = 0x02,
+ AUDIO_CS_REQ_MEM = 0x03,
+} audio_cs_req_t;
+
+/// A.17 - Control Selector Codes
+
+/// A.17.1 - Clock Source Control Selectors
+typedef enum
+{
+ AUDIO_CS_CTRL_UNDEF = 0x00,
+ AUDIO_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO_CS_CTRL_CLK_VALID = 0x02,
+} audio_clock_src_control_selector_t;
+
+/// A.17.2 - Clock Selector Control Selectors
+typedef enum
+{
+ AUDIO_CX_CTRL_UNDEF = 0x00,
+ AUDIO_CX_CTRL_CONTROL = 0x01,
+} audio_clock_sel_control_selector_t;
+
+/// A.17.3 - Clock Multiplier Control Selectors
+typedef enum
+{
+ AUDIO_CM_CTRL_UNDEF = 0x00,
+ AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio_clock_mul_control_selector_t;
+
+/// A.17.4 - Terminal Control Selectors
+typedef enum
+{
+ AUDIO_TE_CTRL_UNDEF = 0x00,
+ AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO_TE_CTRL_CLUSTER = 0x04,
+ AUDIO_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO_TE_CTRL_LATENCY = 0x07,
+} audio_terminal_control_selector_t;
+
+/// A.17.5 - Mixer Control Selectors
+typedef enum
+{
+ AUDIO_MU_CTRL_UNDEF = 0x00,
+ AUDIO_MU_CTRL_MIXER = 0x01,
+ AUDIO_MU_CTRL_CLUSTER = 0x02,
+ AUDIO_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO_MU_CTRL_LATENCY = 0x05,
+} audio_mixer_control_selector_t;
+
+/// A.17.6 - Selector Control Selectors
+typedef enum
+{
+ AUDIO_SU_CTRL_UNDEF = 0x00,
+ AUDIO_SU_CTRL_SELECTOR = 0x01,
+ AUDIO_SU_CTRL_LATENCY = 0x02,
+} audio_sel_control_selector_t;
+
+/// A.17.7 - Feature Unit Control Selectors
+typedef enum
+{
+ AUDIO_FU_CTRL_UNDEF = 0x00,
+ AUDIO_FU_CTRL_MUTE = 0x01,
+ AUDIO_FU_CTRL_VOLUME = 0x02,
+ AUDIO_FU_CTRL_BASS = 0x03,
+ AUDIO_FU_CTRL_MID = 0x04,
+ AUDIO_FU_CTRL_TREBLE = 0x05,
+ AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO_FU_CTRL_AGC = 0x07,
+ AUDIO_FU_CTRL_DELAY = 0x08,
+ AUDIO_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO_FU_CTRL_INVERTER = 0x0D,
+ AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO_FU_CTRL_LATENCY = 0x10,
+} audio_feature_unit_control_selector_t;
+
+/// A.17.8 Effect Unit Control Selectors
+
+/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_PE_CTRL_UNDEF = 0x00,
+ AUDIO_PE_CTRL_ENABLE = 0x01,
+ AUDIO_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO_PE_CTRL_QFACTOR = 0x03,
+ AUDIO_PE_CTRL_GAIN = 0x04,
+ AUDIO_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO_PE_CTRL_LATENCY = 0x07,
+} audio_parametric_equalizer_control_selector_t;
+
+/// A.17.8.2 Reverberation Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_RV_CTRL_UNDEF = 0x00,
+ AUDIO_RV_CTRL_ENABLE = 0x01,
+ AUDIO_RV_CTRL_TYPE = 0x02,
+ AUDIO_RV_CTRL_LEVEL = 0x03,
+ AUDIO_RV_CTRL_TIME = 0x04,
+ AUDIO_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO_RV_CTRL_PREDELAY = 0x06,
+ AUDIO_RV_CTRL_DENSITY = 0x07,
+ AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO_RV_CTRL_LATENCY = 0x0B,
+} audio_reverberation_effect_control_selector_t;
+
+/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_MD_CTRL_UNDEF = 0x00,
+ AUDIO_MD_CTRL_ENABLE = 0x01,
+ AUDIO_MD_CTRL_BALANCE = 0x02,
+ AUDIO_MD_CTRL_RATE = 0x03,
+ AUDIO_MD_CTRL_DEPTH = 0x04,
+ AUDIO_MD_CTRL_TIME = 0x05,
+ AUDIO_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MD_CTRL_LATENCY = 0x09,
+} audio_modulation_delay_control_selector_t;
+
+/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_DR_CTRL_UNDEF = 0x00,
+ AUDIO_DR_CTRL_ENABLE = 0x01,
+ AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO_DR_CTRL_LATENCY = 0x09,
+} audio_dynamic_range_compression_control_selector_t;
+
+/// A.17.9 Processing Unit Control Selectors
+
+/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_UD_CTRL_UNDEF = 0x00,
+ AUDIO_UD_CTRL_ENABLE = 0x01,
+ AUDIO_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO_UD_CTRL_CLUSTER = 0x03,
+ AUDIO_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO_UD_CTRL_LATENCY = 0x06,
+} audio_up_down_mix_control_selector_t;
+
+/// A.17.9.2 Dolby Prologic ™ Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_DP_CTRL_UNDEF = 0x00,
+ AUDIO_DP_CTRL_ENABLE = 0x01,
+ AUDIO_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO_DP_CTRL_CLUSTER = 0x03,
+ AUDIO_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO_DP_CTRL_LATENCY = 0x06,
+} audio_dolby_prologic_control_selector_t;
+
+/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
+} audio_stereo_extender_control_selector_t;
+
+/// A.17.10 Extension Unit Control Selectors
+typedef enum
+{
+ AUDIO_XU_CTRL_UNDEF = 0x00,
+ AUDIO_XU_CTRL_ENABLE = 0x01,
+ AUDIO_XU_CTRL_CLUSTER = 0x02,
+ AUDIO_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO_XU_CTRL_LATENCY = 0x05,
+} audio_extension_unit_control_selector_t;
+
+/// A.17.11 AudioStreaming Interface Control Selectors
+typedef enum
+{
+ AUDIO_AS_CTRL_UNDEF = 0x00,
+ AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio_audiostreaming_interface_control_selector_t;
+
+/// A.17.12 Encoder Control Selectors
+typedef enum
+{
+ AUDIO_EN_CTRL_UNDEF = 0x00,
+ AUDIO_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO_EN_CTRL_QUALITY = 0x02,
+ AUDIO_EN_CTRL_VBR = 0x03,
+ AUDIO_EN_CTRL_TYPE = 0x04,
+ AUDIO_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO_EN_CTRL_PARAM1 = 0x08,
+ AUDIO_EN_CTRL_PARAM2 = 0x09,
+ AUDIO_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO_EN_CTRL_PARAM8 = 0x0F,
+} audio_encoder_control_selector_t;
+
+/// A.17.13 Decoder Control Selectors
+
+/// A.17.13.1 MPEG Decoder Control Selectors
+typedef enum
+{
+ AUDIO_MPD_CTRL_UNDEF = 0x00,
+ AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO_MPD_CTRL_SCALING = 0x05,
+ AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio_MPEG_decoder_control_selector_t;
+
+/// A.17.13.2 AC-3 Decoder Control Selectors
+typedef enum
+{
+ AUDIO_AD_CTRL_UNDEF = 0x00,
+ AUDIO_AD_CTRL_MODE = 0x01,
+ AUDIO_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO_AD_CTRL_SCALING = 0x03,
+ AUDIO_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
+} audio_AC3_decoder_control_selector_t;
+
+/// A.17.13.3 WMA Decoder Control Selectors
+typedef enum
+{
+ AUDIO_WD_CTRL_UNDEF = 0x00,
+ AUDIO_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
+} audio_WMA_decoder_control_selector_t;
+
+/// A.17.13.4 DTS Decoder Control Selectors
+typedef enum
+{
+ AUDIO_DD_CTRL_UNDEF = 0x00,
+ AUDIO_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
+} audio_DTS_decoder_control_selector_t;
+
+/// A.17.14 Endpoint Control Selectors
+typedef enum
+{
+ AUDIO_EP_CTRL_UNDEF = 0x00,
+ AUDIO_EP_CTRL_PITCH = 0x01,
+ AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio_EP_control_selector_t;
+
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
+ AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
+ AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
+} audio_terminal_type_t;
+
+/// 2.2 - Audio Class-Input Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
+ AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
+ AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
+ AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
+ AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
+ AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
+ AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
+} audio_terminal_input_type_t;
+
+/// 2.3 - Audio Class-Output Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
+ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
+ AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
+ AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
+ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
+ AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
+ AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
+ AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
+} audio_terminal_output_type_t;
+
+/// Rest is yet to be implemented
+
+/// Additional Audio Device Class Codes - Source: Audio Data Formats
+
+/// A.1 - Audio Class-Format Type Codes UAC2
+typedef enum
+{
+ AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO_FORMAT_TYPE_I = 0x01,
+ AUDIO_FORMAT_TYPE_II = 0x02,
+ AUDIO_FORMAT_TYPE_III = 0x03,
+ AUDIO_FORMAT_TYPE_IV = 0x04,
+ AUDIO_EXT_FORMAT_TYPE_I = 0x81,
+ AUDIO_EXT_FORMAT_TYPE_II = 0x82,
+ AUDIO_EXT_FORMAT_TYPE_III = 0x83,
+} audio_format_type_t;
-/// Audio Class-Specific AC Interface Descriptor Subtypes
+// A.2.1 - Audio Class-Audio Data Format Type I UAC2
typedef enum
{
- AUDIO_CS_INTERFACE_HEADER = 0x01,
- AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02,
- AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03,
- AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04,
- AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05,
- AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06,
- AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07,
- AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08,
- AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09,
- AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A,
- AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B,
- AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
- AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
-} audio_cs_interface_subtype_t;
+ AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
+ AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
+ AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
+ AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
+ AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
+ AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000,
+} audio_data_format_type_I_t;
+
+/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
+
+/// Isochronous End Point Attributes
+typedef enum
+{
+ TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
+ TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
+ TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
+ TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C,
+ TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
+ TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
+ TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
+} tusb_iso_ep_attribute_t;
+
+/// Audio Class-Control Values UAC2
+typedef enum
+{
+ AUDIO_CTRL_NONE = 0x00, ///< No Host access
+ AUDIO_CTRL_R = 0x01, ///< Host read access only
+ AUDIO_CTRL_RW = 0x03, ///< Host read write access
+} audio_control_t;
+
+/// Audio Class-Specific AC Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio_cs_ac_interface_control_pos_t;
+
+/// Audio Class-Specific AS Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio_cs_as_interface_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio_cs_as_iso_data_ep_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Clock Source Attributes UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio_clock_source_attribute_t;
+
+/// Audio Class-Clock Source Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio_clock_source_control_pos_t;
+
+/// Audio Class-Clock Selector Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio_clock_selector_control_pos_t;
+
+/// Audio Class-Clock Multiplier Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio_clock_multiplier_control_pos_t;
+
+/// Audio Class-Input Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio_terminal_input_control_pos_t;
+
+/// Audio Class-Output Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio_terminal_output_control_pos_t;
+
+/// Audio Class-Feature Unit Controls UAC2
+typedef enum
+{
+ AUDIO_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio_feature_unit_control_pos_t;
+
+/// Audio Class-Audio Channel Configuration UAC2
+typedef enum
+{
+ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+} audio_channel_config_t;
+
+/// AUDIO Channel Cluster Descriptor (4.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
+} audio_desc_channel_cluster_t;
+
+/// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER.
+ uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
+ uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t.
+ uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
+ uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
+} audio_desc_cs_ac_interface_t;
+
+/// AUDIO Clock Source Descriptor (4.7.2.1)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
+ uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
+ uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
+} audio_desc_clock_source_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
+ uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
+ uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio_desc_clock_selector_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins
+#define audio_desc_clock_selector_n_t(source_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; \
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bClockID ; \
+ uint8_t bNrInPins ; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID ; \
+ } sourceID[source_num] ; \
+ uint8_t bmControls ; \
+ uint8_t iClockSource ; \
+}
+
+/// AUDIO Clock Multiplier Descriptor (4.7.2.3)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
+ uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio_desc_clock_multiplier_t;
+
+/// AUDIO Input Terminal Descriptor(4.7.2.4)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
+ uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
+ uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
+ uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
+} audio_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor(4.7.2.5)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 12.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types.
+ uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected.
+ uint16_t bmControls ; ///< See: audio_terminal_output_type_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal.
+} audio_desc_output_terminal_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 14.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT.
+ uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
+ struct TU_ATTR_PACKED {
+ uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
+ } controls[2] ;
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit.
+} audio_desc_feature_unit_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels
+#define audio_desc_feature_unit_n_t(ch_num)\
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; /* 6+(ch_num+1)*4 */\
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bUnitID ; \
+ uint8_t bSourceID ; \
+ struct TU_ATTR_PACKED { \
+ uint32_t bmaControls ; \
+ } controls[ch_num+1] ; \
+ uint8_t iTerminal ; \
+}
+
+/// AUDIO Class-Specific AS Interface Descriptor(4.9.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 16.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t.
+ uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t.
+ uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t.
+ uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 6.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I.
+ uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
+ uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot.
+} audio_desc_type_I_format_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
+ uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
+} audio_desc_cs_as_iso_data_ep_t;
+
+// 5.2.2 Control Request Layout
+typedef struct TU_ATTR_PACKED
+{
+ union
+ {
+ struct TU_ATTR_PACKED
+ {
+ uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
+ uint8_t type : 2; ///< Request type tusb_request_type_t.
+ uint8_t direction : 1; ///< Direction type. tusb_dir_t
+ } bmRequestType_bit;
+
+ uint8_t bmRequestType;
+ };
+
+ uint8_t bRequest; ///< Request type audio_cs_req_t
+ uint8_t bChannelNumber;
+ uint8_t bControlSelector;
+ union
+ {
+ uint8_t bInterface;
+ uint8_t bEndpoint;
+ };
+ uint8_t bEntityID;
+ uint16_t wLength;
+} audio_control_request_t;
+
+//// 5.2.3 Control Request Parameter Block Layout
+
+// 5.2.3.1 1-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_1_t;
+
+// 5.2.3.2 2-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_2_t;
+
+// 5.2.3.3 4-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_4_t;
+
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_1_t;
+
+// 5.2.3.2 2-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_2_t;
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_4_t;
+
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+#define audio_control_range_1_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges] ; \
+}
+
+/// 5.2.3.2 2-byte Control RANGE Parameter Block
+#define audio_control_range_2_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+#define audio_control_range_4_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
/** @} */
#ifdef __cplusplus
- }
+}
#endif
#endif
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
new file mode 100644
index 000000000..ff4e312a7
--- /dev/null
+++ b/src/class/audio/audio_device.c
@@ -0,0 +1,2272 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/*
+ * This driver supports at most one out EP, one in EP, one control EP, and one feedback EP and one alternative interface other than zero. Hence, only one input terminal and one output terminal are support, if you need more adjust the driver!
+ * It supports multiple TX and RX channels.
+ *
+ * In case you need more alternate interfaces, you need to define additional defines for this specific alternate interface. Just define them and set them in the set_interface function.
+ *
+ * There are three data flow structures currently implemented, where at least one SW-FIFO is used to decouple the asynchronous processes MCU vs. host
+ *
+ * 1. Input data -> SW-FIFO -> MCU USB
+ *
+ * The most easiest version, available in case the target MCU can handle the software FIFO (SW-FIFO) and if it is implemented in the device driver (if yes then dcd_edpt_xfer_fifo() is available)
+ *
+ * 2. Input data -> SW-FIFO -> Linear buffer -> MCU USB
+ *
+ * In case the target MCU can not handle a SW-FIFO, a linear buffer is used. This uses the default function dcd_edpt_xfer(). In this case more memory is required.
+ *
+ * 3. (Input data 1 | Input data 2 | ... | Input data N) -> (SW-FIFO 1 | SW-FIFO 2 | ... | SW-FIFO N) -> Linear buffer -> MCU USB
+ *
+ * This case is used if you have more channels which need to be combined into one stream. Every channel has its own SW-FIFO. All data is encoded into an Linear buffer.
+ *
+ * The same holds in the RX case.
+ *
+ * */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "audio_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
+// is available or driver is would need to be changed dramatically
+
+// Only STM32 synopsys use non-linear buffer for now
+// Synopsys detection copied from dcd_synopsys.c (refactor later on)
+#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
+ defined (STM32F107xB) || defined (STM32F107xC)
+#define STM32F1_SYNOPSYS
+#endif
+
+#if defined (STM32L475xx) || defined (STM32L476xx) || \
+ defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
+ defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
+ defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
+#define STM32L4_SYNOPSYS
+#endif
+
+#if (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) || \
+ CFG_TUSB_MCU == OPT_MCU_RX63X || \
+ CFG_TUSB_MCU == OPT_MCU_RX65X || \
+ CFG_TUSB_MCU == OPT_MCU_RX72N || \
+ CFG_TUSB_MCU == OPT_MCU_GD32VF103
+#define USE_LINEAR_BUFFER 0
+#else
+#define USE_LINEAR_BUFFER 1
+#endif
+
+// Declaration of buffers
+
+// Check for maximum supported numbers
+#if CFG_TUD_AUDIO > 3
+#error Maximum number of audio functions restricted to three!
+#endif
+
+// EP IN software buffers and mutexes
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_2; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+// Linear buffer TX in case:
+// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
+// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX];
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+
+// EP OUT software buffers and mutexes
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_2; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+// Linear buffer RX in case:
+// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
+// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX];
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+
+// Control buffers
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
+#if CFG_TUD_AUDIO > 1
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
+#endif
+#if CFG_TUD_AUDIO > 2
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
+#endif
+
+// Active alternate setting of interfaces
+uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
+uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
+uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
+#endif
+
+// Software encoding/decoding support FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif
+#endif
+
+typedef struct
+{
+ uint8_t rhport;
+ uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ uint8_t ep_in; // TX audio data EP.
+ uint16_t ep_in_sz; // Current size of TX EP
+ uint8_t ep_in_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ uint8_t ep_out; // Incoming (into uC) audio data EP.
+ uint16_t ep_out_sz; // Current size of RX EP
+ uint8_t ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb; // Feedback EP.
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ uint8_t ep_int_ctr; // Audio control interrupt EP.
+#endif
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ uint16_t desc_length; // Length of audio function descriptor
+
+ // Buffer for control requests
+ uint8_t * ctrl_buf;
+ uint8_t ctrl_buf_sz;
+
+ // Current active alternate settings
+ uint8_t * alt_setting; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
+
+ // EP Transfer buffers and FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t ep_out_ff;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t ep_in_ff;
+#endif
+
+ // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
+#endif
+
+ // Decoding parameters - parameters are set when alternate AS interface is set by host
+ // Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ audio_format_type_t format_type_rx;
+ uint8_t n_channels_rx;
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ audio_data_format_type_I_t format_type_I_rx;
+ uint8_t n_bytes_per_sampe_rx;
+ uint8_t n_channels_per_ff_rx;
+ uint8_t n_ff_used_rx;
+#endif
+#endif
+
+ // Encoding parameters - parameters are set when alternate AS interface is set by host
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ audio_format_type_t format_type_tx;
+ uint8_t n_channels_tx;
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio_data_format_type_I_t format_type_I_tx;
+ uint8_t n_bytes_per_sampe_tx;
+ uint8_t n_channels_per_ff_tx;
+ uint8_t n_ff_used_tx;
+#endif
+#endif
+
+ // Support FIFOs for software encoding and decoding
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t * rx_supp_ff;
+ uint8_t n_rx_supp_ff;
+ uint16_t rx_supp_ff_sz_max;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t * tx_supp_ff;
+ uint8_t n_tx_supp_ff;
+ uint16_t tx_supp_ff_sz_max;
+#endif
+
+ // Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR the support FIFOs are used
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+ uint8_t * lin_buf_out;
+#define USE_LINEAR_BUFFER_RX 1
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+ uint8_t * lin_buf_in;
+#define USE_LINEAR_BUFFER_TX 1
+#endif
+
+} audiod_function_t;
+
+#ifndef USE_LINEAR_BUFFER_TX
+#define USE_LINEAR_BUFFER_TX 0
+#endif
+
+#ifndef USE_LINEAR_BUFFER_RX
+#define USE_LINEAR_BUFFER_RX 0
+#endif
+
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t* audio);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio);
+#endif
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+
+static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int);
+static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio, uint8_t *idxItf, uint8_t const **pp_desc_int);
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id);
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
+static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio);
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf);
+
+static inline uint8_t tu_desc_subtype(void const* desc)
+{
+ return ((uint8_t const*) desc)[2];
+}
+#endif
+
+bool tud_audio_n_mounted(uint8_t func_id)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO);
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (audio->ep_in == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ if (audio->ep_int_ctr == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->ep_fb == 0) return false;
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+uint16_t tud_audio_n_available(uint8_t func_id)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_count(&_audiod_fct[func_id].ep_out_ff);
+}
+
+uint16_t tud_audio_n_read(uint8_t func_id, void* buffer, uint16_t bufsize)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_read_n(&_audiod_fct[func_id].ep_out_ff, buffer, bufsize);
+}
+
+bool tud_audio_n_clear_ep_out_ff(uint8_t func_id)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
+}
+
+tu_fifo_t* tud_audio_n_get_ep_out_ff(uint8_t func_id)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
+ return NULL;
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+// Delete all content in the support RX FIFOs
+bool tud_audio_n_clear_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_clear(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_available_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_count(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_read_support_ff(uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_read_n(&_audiod_fct[func_id].rx_supp_ff[ff_idx], buffer, bufsize);
+}
+
+tu_fifo_t* tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff) return &_audiod_fct[func_id].rx_supp_ff[ff_idx];
+ return NULL;
+}
+#endif
+
+// This function is called once an audio packet is received by the USB and is responsible for putting data from USB memory into EP_OUT_FIFO (or support FIFOs + decoding of received stream into audio channels).
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_DECODING = 0.
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
+{
+ uint8_t idxItf;
+ uint8_t const *dummy2;
+ uint8_t idx_audio_fct = 0;
+
+ if (tud_audio_rx_done_pre_read_cb || tud_audio_rx_done_post_read_cb)
+ {
+ idx_audio_fct = audiod_get_audio_fct_idx(audio);
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2));
+ }
+
+ // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO (or decoded into support RX software FIFO)
+ if (tud_audio_rx_done_pre_read_cb) TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ switch (audio->format_type_rx)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (audio->format_type_I_tx)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+ TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received));
+ break;
+
+ default:
+ // DESIRED CFG_TUD_AUDIO_FORMAT_TYPE_I_RX NOT IMPLEMENTED!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_RX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+
+ // Prepare for next transmission
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+
+#else
+
+#if USE_LINEAR_BUFFER_RX
+ // Data currently is in linear buffer, copy into EP OUT FIFO
+ TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
+
+ // Schedule for next receive
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+#else
+ // Data is already placed in EP FIFO, schedule for next receive
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+#endif
+
+#endif
+
+ // Call a weak callback here - a possibility for user to get informed decoding was completed
+ if (tud_audio_rx_done_post_read_cb) TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// The following functions are used in case CFG_TUD_AUDIO_ENABLE_DECODING != 0
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// Decoding according to 2.3.1.5 Audio Streams
+
+// Helper function
+static inline uint8_t * audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesToCopy, void * dst, uint8_t * dst_end, uint8_t * src, uint8_t const n_ff_used)
+{
+
+ // This function is an optimized version of
+ // while((uint8_t *)dst < dst_end)
+ // {
+ // memcpy(dst, src, nBytesToCopy);
+ // dst = (uint8_t *)dst + nBytesToCopy;
+ // src += nBytesToCopy * n_ff_used;
+ // }
+
+ // Optimize for fast half word copies
+ typedef struct{
+ uint16_t val;
+ } __attribute((__packed__)) unaligned_uint16_t;
+
+ // Optimize for fast word copies
+ typedef struct{
+ uint32_t val;
+ } __attribute((__packed__)) unaligned_uint32_t;
+
+ switch (nBytesToCopy)
+ {
+ case 1:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(uint8_t *)dst++ = *src;
+ src += n_ff_used;
+ }
+ break;
+
+ case 2:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
+ dst += 2;
+ src += 2 * n_ff_used;
+ }
+ break;
+
+ case 3:
+ while((uint8_t *)dst < dst_end)
+ {
+ // memcpy(dst, src, 3);
+ // dst = (uint8_t *)dst + 3;
+ // src += 3 * n_ff_used;
+
+ // TODO: Is there a faster way to copy 3 bytes?
+ *(uint8_t *)dst++ = *src++;
+ *(uint8_t *)dst++ = *src++;
+ *(uint8_t *)dst++ = *src++;
+
+ src += 3 * (n_ff_used - 1);
+ }
+ break;
+
+ case 4:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
+ dst += 4;
+ src += 4 * n_ff_used;
+ }
+ break;
+ }
+
+ return src;
+}
+
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
+{
+ (void) rhport;
+
+ // Determine amount of samples
+ uint8_t const n_ff_used = audio->n_ff_used_rx;
+ uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
+ uint8_t cnt_ff;
+
+ // Decode
+ uint8_t * src;
+ uint8_t * dst_end;
+
+ tu_fifo_buffer_info_t info;
+
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ tu_fifo_get_write_info(&audio->rx_supp_ff[cnt_ff], &info);
+
+ if (info.len_lin != 0)
+ {
+ info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
+ src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+ dst_end = info.ptr_lin + info.len_lin;
+ src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
+
+ // Handle wrapped part of FIFO
+ info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
+ if (info.len_wrap != 0)
+ {
+ dst_end = info.ptr_wrap + info.len_wrap;
+ audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
+ }
+ tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
+ }
+ }
+
+ // Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it
+ // TU_VERIFY(cnt != n_bytes);
+
+ return true;
+}
+#endif //CFG_TUD_AUDIO_ENABLE_DECODING
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+/**
+ * \brief Write data to EP in buffer
+ *
+ * Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
+ * If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
+ *
+ * \param[in] func_id: Index of audio function interface
+ * \param[in] data: Pointer to data array to be copied from
+ * \param[in] len: # of array elements to copy
+ * \return Number of bytes actually written
+ */
+uint16_t tud_audio_n_write(uint8_t func_id, const void * data, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
+}
+
+bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) // Delete all content in the EP IN FIFO
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
+}
+
+tu_fifo_t* tud_audio_n_get_ep_in_ff(uint8_t func_id)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
+ return NULL;
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+
+uint16_t tud_audio_n_flush_tx_support_ff(uint8_t func_id) // Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+ uint16_t n_bytes_copied = tu_fifo_count(&audio->tx_supp_ff[0]);
+
+ TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio));
+
+ n_bytes_copied -= tu_fifo_count(&audio->tx_supp_ff[0]);
+ n_bytes_copied = n_bytes_copied*audio->tx_supp_ff[0].item_size;
+
+ return n_bytes_copied;
+}
+
+bool tud_audio_n_clear_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
+ return tu_fifo_clear(&_audiod_fct[func_id].tx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_write_support_ff(uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
+ return tu_fifo_write_n(&_audiod_fct[func_id].tx_supp_ff[ff_idx], data, len);
+}
+
+tu_fifo_t* tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff) return &_audiod_fct[func_id].tx_supp_ff[ff_idx];
+ return NULL;
+}
+
+#endif
+
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_ctr_done_cb() is called in inform user
+uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ // We write directly into the EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr));
+
+ // Check length
+ TU_VERIFY(len <= CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE);
+
+ memcpy(_audiod_fct[func_id].ep_int_ctr_buf, buffer, len);
+
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr, _audiod_fct[func_id].ep_int_ctr_buf, len));
+
+ return true;
+}
+
+#endif
+
+
+// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_ENCODING = 0 and use tud_audio_n_write.
+
+// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
+{
+ uint8_t idxItf;
+ uint8_t const *dummy2;
+
+ uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, &idxItf, &dummy2));
+
+ // Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications
+ if (audio->alt_setting[idxItf] == 0) return false;
+
+ // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or
+ // if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
+ if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+
+ // Send everything in ISO EP FIFO
+ uint16_t n_bytes_tx;
+
+ // If support FIFOs are used, encode and schedule transmit
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+ switch (audio->format_type_tx)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL ENCODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (audio->format_type_I_tx)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+
+ n_bytes_tx = audiod_encode_type_I_pcm(rhport, audio);
+ break;
+
+ default:
+ // YOUR ENCODING IS REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_TX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+ }
+
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+
+#else
+ // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
+
+ n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz); // Limit up to max packet size, more can not be done for ISO
+
+#if USE_LINEAR_BUFFER_TX
+ tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+#else
+ // Send everything in ISO EP FIFO
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+#endif
+
+#endif
+
+ // Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
+ if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+// Take samples from the support buffer and encode them into the IN EP software FIFO
+// Returns number of bytes written into linear buffer
+
+/* 2.3.1.7.1 PCM Format
+The PCM (Pulse Coded Modulation) format is the most commonly used audio format to represent audio
+data streams. The audio data is not compressed and uses a signed two’s-complement fixed point format. It
+is left-justified (the sign bit is the Msb) and data is padded with trailing zeros to fill the remaining unused
+bits of the subslot. The binary point is located to the right of the sign bit so that all values lie within the
+range [-1, +1)
+ */
+
+/*
+ * This function encodes channels saved within the support FIFOs into one stream by interleaving the PCM samples
+ * in the support FIFOs according to 2.3.1.5 Audio Streams. It does not control justification (left or right) and
+ * does not change the number of bytes per sample.
+ * */
+
+// Helper function
+static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, uint8_t * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used)
+{
+ // Optimize for fast half word copies
+ typedef struct{
+ uint16_t val;
+ } __attribute((__packed__)) unaligned_uint16_t;
+
+ // Optimize for fast word copies
+ typedef struct{
+ uint32_t val;
+ } __attribute((__packed__)) unaligned_uint32_t;
+
+ switch (nBytesToCopy)
+ {
+ case 1:
+ while(src < src_end)
+ {
+ *dst = *src++;
+ dst += n_ff_used;
+ }
+ break;
+
+ case 2:
+ while(src < src_end)
+ {
+ *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
+ src += 2;
+ dst += 2 * n_ff_used;
+ }
+ break;
+
+ case 3:
+ while(src < src_end)
+ {
+ // memcpy(dst, src, 3);
+ // src = (uint8_t *)src + 3;
+ // dst += 3 * n_ff_used;
+
+ // TODO: Is there a faster way to copy 3 bytes?
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+
+ dst += 3 * (n_ff_used - 1);
+ }
+ break;
+
+ case 4:
+ while(src < src_end)
+ {
+ *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
+ src += 4;
+ dst += 4 * n_ff_used;
+ }
+ break;
+ }
+
+ return dst;
+}
+
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio)
+{
+ // This function relies on the fact that the length of the support FIFOs was configured to be a multiple of the active sample size in bytes s.t. no sample is split within a wrap
+ // This is ensured within set_interface, where the FIFOs are reconfigured according to this size
+
+ // We encode directly into IN EP's linear buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint8_t const n_ff_used = audio->n_ff_used_tx;
+ uint16_t const nBytesToCopy = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
+ uint16_t const capPerFF = audio->ep_in_sz / n_ff_used; // Sample capacity per FIFO in bytes
+ uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
+ uint8_t cnt_ff;
+
+ for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ uint16_t const count = tu_fifo_count(&audio->tx_supp_ff[cnt_ff]);
+ if (count < nBytesPerFFToSend)
+ {
+ nBytesPerFFToSend = count;
+ }
+ }
+
+ // Check if there is enough
+ if (nBytesPerFFToSend == 0) return 0;
+
+ // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
+ nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, capPerFF);
+
+ // Round to full number of samples (flooring)
+ nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
+
+ // Encode
+ uint8_t * dst;
+ uint8_t * src_end;
+
+ tu_fifo_buffer_info_t info;
+
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
+
+ tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
+
+ if (info.len_lin != 0)
+ {
+ info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length
+ src_end = (uint8_t *)info.ptr_lin + info.len_lin;
+ dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
+
+ // Limit up to desired length
+ info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
+
+ // Handle wrapped part of FIFO
+ if (info.len_wrap != 0)
+ {
+ src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
+ audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
+ }
+
+ tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
+ }
+ }
+
+ return nBytesPerFFToSend * n_ff_used;
+}
+#endif //CFG_TUD_AUDIO_ENABLE_ENCODING
+
+// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio)
+{
+ return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void audiod_init(void)
+{
+ tu_memclr(_audiod_fct, sizeof(_audiod_fct));
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ audiod_function_t* audio = &_audiod_fct[i];
+
+ // Initialize control buffers
+ switch (i)
+ {
+ case 0:
+ audio->ctrl_buf = ctrl_buf_1;
+ audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
+ break;
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
+ case 1:
+ audio->ctrl_buf = ctrl_buf_2;
+ audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
+ case 2:
+ audio->ctrl_buf = ctrl_buf_3;
+ audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
+ break;
+#endif
+ }
+
+ // Initialize active alternate interface buffers
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_N_AS_INT > 0
+ case 0:
+ audio->alt_setting = alt_setting_1;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
+ case 1:
+ audio->alt_setting = alt_setting_2;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
+ case 2:
+ audio->alt_setting = alt_setting_3;
+ break;
+#endif
+ }
+
+ // Initialize IN EP FIFO if required
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+ case 0:
+ tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL);
+#endif
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+ case 1:
+ tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_2), NULL);
+#endif
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+ case 2:
+ tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_3), NULL);
+#endif
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Initialize linear buffers
+#if USE_LINEAR_BUFFER_TX
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ case 0:
+ audio->lin_buf_in = lin_buf_in_1;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+ case 1:
+ audio->lin_buf_in = lin_buf_in_2;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+ case 2:
+ audio->lin_buf_in = lin_buf_in_3;
+ break;
+#endif
+ }
+#endif // USE_LINEAR_BUFFER_TX
+
+ // Initialize OUT EP FIFO if required
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+ case 0:
+ tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_1));
+#endif
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+ case 1:
+ tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_2));
+#endif
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+ case 2:
+ tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_3));
+#endif
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+ // Initialize linear buffers
+#if USE_LINEAR_BUFFER_RX
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ case 0:
+ audio->lin_buf_out = lin_buf_out_1;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ case 1:
+ audio->lin_buf_out = lin_buf_out_2;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ case 2:
+ audio->lin_buf_out = lin_buf_out_3;
+ break;
+#endif
+ }
+#endif // USE_LINEAR_BUFFER_TX
+
+ // Initialize TX support FIFOs if required
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->tx_supp_ff = tx_supp_ff_1;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_1[cnt], tx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_1[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_1[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->tx_supp_ff = tx_supp_ff_2;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_2[cnt], tx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_2[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_2[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->tx_supp_ff = tx_supp_ff_3;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_3[cnt], tx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_3[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_3[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Set encoding parameters for Type_I formats
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+
+ // Initialize RX support FIFOs if required
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->rx_supp_ff = rx_supp_ff_1;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_1[cnt], rx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_1[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_1[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->rx_supp_ff = rx_supp_ff_2;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_2[cnt], rx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_2[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_2[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->rx_supp_ff = rx_supp_ff_3;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_3[cnt], rx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_3[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_3[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Set encoding parameters for Type_I formats
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ }
+}
+
+void audiod_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ audiod_function_t* audio = &_audiod_fct[i];
+ tu_memclr(audio, ITF_MEM_RESET_SIZE);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_clear(&audio->ep_in_ff);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_clear(&audio->ep_out_ff);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->tx_supp_ff[cnt]);
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_supp_ff[cnt]);
+ }
+#endif
+ }
+}
+
+uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) max_len;
+
+ TU_VERIFY ( TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+
+ // Verify version is correct - this check can be omitted
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+
+ // Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
+ if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
+ {
+ TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
+ }
+
+ // Alternate setting MUST be zero - this check can be omitted
+ TU_VERIFY(itf_desc->bAlternateSetting == 0);
+
+ // Find available audio driver interface
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (!_audiod_fct[i].p_desc)
+ {
+ _audiod_fct[i].p_desc = (uint8_t const *)itf_desc; // Save pointer to AC descriptor which is by specification always the first one
+ _audiod_fct[i].rhport = rhport;
+
+ // Setup descriptor lengths
+ switch (i)
+ {
+ case 0:
+ _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
+ break;
+#if CFG_TUD_AUDIO > 1
+ case 1:
+ _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_2_DESC_LEN;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2
+ case 2:
+ _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_3_DESC_LEN;
+ break;
+#endif
+ }
+
+ break;
+ }
+ }
+
+ // Verify we found a free one
+ TU_ASSERT( i < CFG_TUD_AUDIO );
+
+ // This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
+ uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+
+ return drv_len;
+}
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+
+ // Find index of audio streaming interface
+ uint8_t func_id, idxItf;
+ uint8_t const *dummy;
+
+ TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1));
+
+ TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]);
+
+ return true;
+}
+
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Here we need to do the following:
+
+ // 1. Find the audio driver assigned to the given interface to be set
+ // Since one audio driver interface has to be able to cover an unknown number of interfaces (AC, AS + its alternate settings), the best memory efficient way to solve this is to always search through the descriptors.
+ // The audio driver is mapped to an audio function by a reference pointer to the corresponding AC interface of this audio function which serves as a starting point for searching
+
+ // 2. Close EPs which are currently open
+ // To do so it is not necessary to know the current active alternate interface since we already save the current EP addresses - we simply close them
+
+ // 3. Open new EP
+
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
+
+ // Find index of audio streaming interface and index of interface
+ uint8_t func_id, idxItf;
+ uint8_t const *p_desc;
+ TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc));
+
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+ // Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (audio->ep_in_as_intf_num == itf)
+ {
+ audio->ep_in_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_in);
+
+ // Clear FIFOs, since data is no longer valid
+#if !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_clear(&audio->ep_in_ff);
+#else
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->tx_supp_ff[cnt]);
+ }
+#endif
+
+ // Invoke callback - can be used to stop data sampling
+ if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+
+ audio->ep_in = 0; // Necessary?
+
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out_as_intf_num == itf)
+ {
+ audio->ep_out_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_out);
+
+ // Clear FIFOs, since data is no longer valid
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_clear(&audio->ep_out_ff);
+#else
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_supp_ff[cnt]);
+ }
+#endif
+
+ // Invoke callback - can be used to stop data sampling
+ if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+
+ audio->ep_out = 0; // Necessary?
+
+ // Close corresponding feedback EP
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ usbd_edpt_close(rhport, audio->ep_fb);
+ audio->ep_fb = 0; // Necessary?
+#endif
+ }
+#endif
+
+ // Save current alternative interface setting
+ audio->alt_setting[idxItf] = alt;
+
+ // Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
+ // Get pointer at end
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+
+ // p_desc starts at required interface with alternate setting zero
+ while (p_desc < p_desc_end)
+ {
+ // Find correct interface
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
+ {
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+ uint8_t const * p_desc_parse_for_params = p_desc;
+#endif
+ // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
+ uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
+ while (foundEPs < nEps && p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
+ {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *)p_desc));
+
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ //TODO: We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
+ usbd_edpt_clear_stall(rhport, ep_addr);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 0x00) // Check if usage is data EP
+ {
+ // Save address
+ audio->ep_in = ep_addr;
+ audio->ep_in_as_intf_num = itf;
+ audio->ep_in_sz = ((tusb_desc_endpoint_t const *) p_desc)->wMaxPacketSize.size;
+
+ // If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+ audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
+
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ const uint16_t active_fifo_depth = (audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx;
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
+ }
+ audio->n_ff_used_tx = audio->n_channels_tx / audio->n_channels_per_ff_tx;
+ TU_ASSERT( audio->n_ff_used_tx <= audio->n_tx_supp_ff );
+#endif
+
+#endif
+ // Invoke callback - can be used to trigger data sampling if not already running
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+
+ // Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
+ // It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
+ {
+ // Save address
+ audio->ep_out = ep_addr;
+ audio->ep_out_as_intf_num = itf;
+ audio->ep_out_sz = ((tusb_desc_endpoint_t const *) p_desc)->wMaxPacketSize.size;
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING
+ audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
+
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx;
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
+ }
+ audio->n_ff_used_rx = audio->n_channels_rx / audio->n_channels_per_ff_rx;
+ TU_ASSERT( audio->n_ff_used_rx <= audio->n_rx_supp_ff );
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // In case of asynchronous EP, call Cb after ep_fb is set
+ if (!(((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.sync == 0x01 && audio->ep_fb == 0))
+ {
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+#else
+ // Invoke callback
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+#endif
+ // Prepare for incoming data
+#if USE_LINEAR_BUFFER_RX
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+#else
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+#endif
+ }
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 1) // Check if usage is explicit data feedback
+ {
+ audio->ep_fb = ep_addr;
+
+ // Invoke callback after ep_out is set
+ if (audio->ep_out != 0)
+ {
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+ }
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ foundEPs += 1;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ TU_VERIFY(foundEPs == nEps);
+
+ // We are done - abort loop
+ break;
+ }
+
+ // Moving forward
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ tud_control_status(rhport, p_request);
+
+ return true;
+}
+
+// Invoked when class request DATA stage is finished.
+// return false to stall control EP (e.g Host send non-sense DATA)
+static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ // Handle audio class specific set requests
+ if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT)
+ {
+ uint8_t func_id;
+
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ if (entityID != 0)
+ {
+ if (tud_audio_set_req_entity_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+
+ // Invoke callback
+ return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No entity set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ else
+ {
+ if (tud_audio_set_req_itf_cb)
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+
+ // Invoke callback
+ return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No interface set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ if (tud_audio_set_req_ep_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+
+ // Invoke callback
+ return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No EP set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ break;
+ // Unknown/Unsupported recipient
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+ return true;
+}
+
+// Handle class control request
+// return false to stall control endpoint (e.g unsupported request)
+static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Handle standard requests - standard set requests usually have no data stage so we also handle set requests here
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+ {
+ switch (p_request->bRequest)
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ return audiod_get_interface(rhport, p_request);
+
+ case TUSB_REQ_SET_INTERFACE:
+ return audiod_set_interface(rhport, p_request);
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+
+ // Handle class requests
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t func_id;
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_entity_cb)
+ {
+ return tud_audio_get_req_entity_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No entity get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_itf_cb)
+ {
+ return tud_audio_get_req_itf_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No interface get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_ep_cb)
+ {
+ return tud_audio_get_req_ep_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No EP get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
+ TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz));
+ return true;
+ }
+
+ // There went something wrong - unsupported control request type
+ TU_BREAKPOINT();
+ return false;
+}
+
+bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ return audiod_control_request(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ return audiod_control_complete(rhport, request);
+ }
+
+ return true;
+}
+
+bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+ (void) xferred_bytes;
+
+ // Search for interface belonging to given end point address and proceed as required
+ uint8_t func_id;
+ for (func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
+ {
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+ // Data transmission of control interrupt finished
+ if (_audiod_fct[func_id].ep_int_ctr == ep_addr)
+ {
+ // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
+ // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
+ // In case of an erroneous transmission a retransmission is conducted - this is taken care of by PHY ???
+
+ // I assume here, that things above are handled by PHY
+ // All transmission is done - what remains to do is to inform job was completed
+
+ if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, (uint16_t) xferred_bytes));
+ }
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+
+ // Data transmission of audio packet finished
+ if (_audiod_fct[func_id].ep_in == ep_addr && _audiod_fct[func_id].alt_setting != 0)
+ {
+ // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
+ // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
+ // This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
+
+ // Check if there is data to load into EPs buffer - if not load it with ZLP
+ // Be aware - we as a device are not able to know if the host polls for data with a faster rate as we stated this in the descriptors. Therefore we always have to put something into the EPs buffer. However, once we did that, there is no way of aborting this or replacing what we put into the buffer before!
+ // This is the only place where we can fill something into the EPs buffer!
+
+ // Load new data
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
+
+ // Transmission of ZLP is done by audiod_tx_done_cb()
+ return true;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ // New audio packet received
+ if (_audiod_fct[func_id].ep_out == ep_addr)
+ {
+ TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_fct[func_id], (uint16_t) xferred_bytes));
+ return true;
+ }
+
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Transmission of feedback EP finished
+ if (_audiod_fct[func_id].ep_fb == ep_addr)
+ {
+ if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
+
+ // Schedule a transmit with the new value if EP is not busy
+ if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb))
+ {
+ // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
+ return audiod_fb_send(rhport, &_audiod_fct[func_id]);
+ }
+ }
+#endif
+#endif
+ }
+
+ return false;
+}
+
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len)
+{
+ // Handles only sending of data not receiving
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false;
+
+ // Get corresponding driver index
+ uint8_t func_id;
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // Crop length
+ if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
+
+ // Copy into buffer
+ memcpy((void *)_audiod_fct[func_id].ctrl_buf, data, (size_t)len);
+
+ // Schedule transmit
+ return tud_control_xfer(rhport, p_request, (void*)_audiod_fct[func_id].ctrl_buf, len);
+}
+
+// This helper function finds for a given audio function and AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ if (audio->p_desc)
+ {
+ // Get pointer at end
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+
+ // Advance past AC descriptors
+ uint8_t const *p_desc = tu_desc_next(audio->p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ uint8_t tmp = 0;
+ while (p_desc < p_desc_end)
+ {
+ // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0)
+ {
+ if (((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
+ {
+ *idxItf = tmp;
+ *pp_desc_int = p_desc;
+ return true;
+ }
+ // Increase index, bytes read, and pointer
+ tmp++;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ return false;
+}
+
+// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ // Loop over audio driver interfaces
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (audiod_get_AS_interface_index(itf, &_audiod_fct[i], idxItf, pp_desc_int))
+ {
+ *func_id = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Verify an entity with the given ID exists and returns also the corresponding driver index
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ // Look for the correct driver by checking if the unique standard AC interface number fits
+ if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
+ {
+ // Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
+ uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc); // Points to CS AC descriptor
+ uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
+ p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
+
+ while (p_desc < p_desc_end)
+ {
+ if (p_desc[3] == entityID) // Entity IDs are always at offset 3
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_fct[i].p_desc)
+ {
+ // Get pointer at beginning and end
+ uint8_t const *p_desc = _audiod_fct[i].p_desc;
+ uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_fct[i].p_desc)
+ {
+ // Get pointer at end
+ uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
+
+ // Advance past AC descriptors - EP we look for are streaming EPs
+ uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+// p_desc points to the AS interface of alternate setting zero
+// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
+// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
+static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf)
+{
+ p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
+
+ while (p_desc < p_desc_end)
+ {
+ // Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
+
+ // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL)
+ {
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num)
+ {
+ audio->n_channels_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
+ audio->format_type_tx = (audio_format_type_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType);
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio->format_type_I_tx = (audio_data_format_type_I_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats);
+#endif
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf == audio->ep_out_as_intf_num)
+ {
+ audio->n_channels_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
+ audio->format_type_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ audio->format_type_I_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats;
+#endif
+ }
+#endif
+ }
+
+ // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const * )p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I)
+ {
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num)
+ {
+ audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf == audio->ep_out_as_intf_num)
+ {
+ audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ }
+#endif
+ }
+#endif
+
+ // Other format types are not supported yet
+
+ p_desc = tu_desc_next(p_desc);
+ }
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+
+// Input value feedback has to be in 16.16 format - the format will be converted according to speed settings automatically
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ // Format the feedback value
+#if !TUD_OPT_HIGH_SPEED
+ uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val;
+
+ // For FS format is 10.14
+ *(fb++) = (feedback >> 2) & 0xFF;
+ *(fb++) = (feedback >> 10) & 0xFF;
+ *(fb++) = (feedback >> 18) & 0xFF;
+ // 4th byte is needed to work correctly with MS Windows
+ *fb = 0;
+#else
+ // For HS format is 16.16 as originally demanded
+ _audiod_fct[func_id].fb_val = feedback;
+#endif
+
+ // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
+ if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
+ {
+ return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]);
+ }
+
+ return true;
+}
+#endif
+
+// No security checks here - internal function only which should always succeed
+uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio)
+{
+ for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO; cnt++)
+ {
+ if (&_audiod_fct[cnt] == audio) return cnt;
+ }
+ return 0;
+}
+
+#endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
new file mode 100644
index 000000000..5a469523c
--- /dev/null
+++ b/src/class/audio/audio_device.h
@@ -0,0 +1,627 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_AUDIO_DEVICE_H_
+#define _TUSB_AUDIO_DEVICE_H_
+
+#include "audio.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// All sizes are in bytes!
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#endif
+
+// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#endif
+
+// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
+#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+#endif
+
+// End point sizes IN BYTES - Limits: Full Speed <= 1023, High Speed <= 1024
+#ifndef CFG_TUD_AUDIO_ENABLE_EP_IN
+#define CFG_TUD_AUDIO_ENABLE_EP_IN 0 // TX
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define CFG_TUD_AUDIO_ENABLE_EP_OUT 0 // RX
+#endif
+
+// Maximum EP sizes for all alternate AS interface settings - used for checks and buffer allocation
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// Software EP FIFO buffer sizes - must be >= max EP SIZEs!
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ 0
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#if CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#if CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#if CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#if CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+#endif
+
+// Enable/disable feedback EP (required for asynchronous RX applications)
+#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
+#endif
+
+// Audio interrupt control EP size - disabled if 0
+#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
+#endif
+
+#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
+#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
+#endif
+
+// Use software encoding/decoding
+
+// The software coding feature of the driver is not mandatory. It is useful if, for instance, you have two I2S streams which need to be interleaved
+// into a single PCM stream as SAMPLE_1 | SAMPLE_2 | SAMPLE_3 | SAMPLE_4.
+//
+// Currently, only PCM type I encoding/decoding is supported!
+//
+// If the coding feature is to be used, support FIFOs need to be configured. Their sizes and numbers are defined below.
+
+// Encoding/decoding is done in software and thus time consuming. If you can encode/decode your stream more efficiently do not use the
+// support FIFOs but write/read directly into/from the EP_X_SW_BUFFER_FIFOs using
+// - tud_audio_n_write() or
+// - tud_audio_n_read().
+// To write/read to/from the support FIFOs use
+// - tud_audio_n_write_support_ff() or
+// - tud_audio_n_read_support_ff().
+//
+// The encoding/decoding format type done is defined below.
+//
+// The encoding/decoding starts when the private callback functions
+// - audio_tx_done_cb()
+// - audio_rx_done_cb()
+// are invoked. If support FIFOs are used, the corresponding encoding/decoding functions are called from there.
+// Once encoding/decoding is done the result is put directly into the EP_X_SW_BUFFER_FIFOs. You can use the public callback functions
+// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
+// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
+// if you want to get informed what happened.
+//
+// If you don't use the support FIFOs you may use the public callback functions
+// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
+// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
+// to write/read from/into the EP_X_SW_BUFFER_FIFOs at the right time.
+//
+// If you need a different encoding which is not support so far implement it in the
+// - audio_tx_done_cb()
+// - audio_rx_done_cb()
+// functions.
+
+// Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size
+// The actual coding parameters of active AS alternate interface is parsed from the descriptors
+
+// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sampe_sz) * sampe_sz)!
+// This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!!
+
+// For PCM encoding/decoding
+
+#ifndef CFG_TUD_AUDIO_ENABLE_ENCODING
+#define CFG_TUD_AUDIO_ENABLE_ENCODING 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_DECODING
+#define CFG_TUD_AUDIO_ENABLE_DECODING 0
+#endif
+
+// This enabling allows to save the current coding parameters e.g. # of bytes per sample etc. - TYPE_I includes common PCM encoding
+#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#define CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING 0
+#endif
+
+// Type I Coding parameters not given within UAC2 descriptors
+// It would be possible to allow for a more flexible setting and not fix this parameter as done below. However, this is most often not needed and kept for later if really necessary. The more flexible setting could be implemented within set_interface(), however, how the values are saved per alternate setting is to be determined!
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#endif
+
+// Remaining types not support so far
+
+// Number of support FIFOs to set up - multiple channels can be handled by one FIFO - very common is two channels per FIFO stemming from one I2S interface
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO 0
+#endif
+
+// Size of support FIFOs IN BYTES - if size > 0 there are as many FIFOs set up as CFG_TUD_AUDIO_FUNC_X_N_TX_SUPP_SW_FIFO and CFG_TUD_AUDIO_FUNC_X_N_RX_SUPP_SW_FIFO
+#ifndef CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of TX channels) / (# of TX support FIFOs) * max(# of bytes per sample)
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of RX channels) / (# of RX support FIFOs) * max(# of bytes per sample)
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ 0
+#endif
+
+//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
+
+// Supported types of this driver:
+// AUDIO_DATA_FORMAT_TYPE_I_PCM - Required definitions: CFG_TUD_AUDIO_N_CHANNELS and CFG_TUD_AUDIO_BYTES_PER_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \addtogroup AUDIO_Serial Serial
+ * @{
+ * \defgroup AUDIO_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+// CFG_TUD_AUDIO > 1
+//--------------------------------------------------------------------+
+bool tud_audio_n_mounted (uint8_t func_id);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+uint16_t tud_audio_n_available (uint8_t func_id);
+uint16_t tud_audio_n_read (uint8_t func_id, void* buffer, uint16_t bufsize);
+bool tud_audio_n_clear_ep_out_ff (uint8_t func_id); // Delete all content in the EP OUT FIFO
+tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+bool tud_audio_n_clear_rx_support_ff (uint8_t func_id, uint8_t ff_idx); // Delete all content in the support RX FIFOs
+uint16_t tud_audio_n_available_support_ff (uint8_t func_id, uint8_t ff_idx);
+uint16_t tud_audio_n_read_support_ff (uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize);
+tu_fifo_t* tud_audio_n_get_rx_support_ff (uint8_t func_id, uint8_t ff_idx);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
+bool tud_audio_n_clear_ep_in_ff (uint8_t func_id); // Delete all content in the EP IN FIFO
+tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+uint16_t tud_audio_n_flush_tx_support_ff (uint8_t func_id); // Force all content in the support TX FIFOs to be written into EP SW FIFO
+bool tud_audio_n_clear_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
+uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len);
+tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Interface0)
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted (void);
+
+// RX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+static inline uint16_t tud_audio_available (void);
+static inline bool tud_audio_clear_ep_out_ff (void); // Delete all content in the EP OUT FIFO
+static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
+static inline tu_fifo_t* tud_audio_get_ep_out_ff (void);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+static inline bool tud_audio_clear_rx_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_available_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_read_support_ff (uint8_t ff_idx, void* buffer, uint16_t bufsize);
+static inline tu_fifo_t* tud_audio_get_rx_support_ff (uint8_t ff_idx);
+#endif
+
+// TX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+static inline uint16_t tud_audio_write (const void * data, uint16_t len);
+static inline bool tud_audio_clear_ep_in_ff (void);
+static inline tu_fifo_t* tud_audio_get_ep_in_ff (void);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+static inline uint16_t tud_audio_flush_tx_support_ff (void);
+static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_write_support_ff (uint8_t ff_idx, const void * data, uint16_t len);
+static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
+#endif
+
+// INT CTR API
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
+#endif
+
+// Buffer control EP data and schedule a transmit
+// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a
+// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
+// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such
+// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time.
+// If the request's wLength is zero, a status packet is sent instead.
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
+// User code should call this function with feedback value in 16.16 format for FS and HS.
+// Value will be corrected for FS to 10.14 format automatically.
+// (see Universal Serial Bus Specification Revision 2.0 5.12.4.2).
+// Feedback value will be sent at FB endpoint interval till it's changed.
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
+static inline bool tud_audio_fb_set(uint32_t feedback);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
+#endif
+
+// Invoked when audio set interface request received
+TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio set interface request received which closes an EP
+TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific set request received for an EP
+TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an interface
+TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an entity
+TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific get request received for an EP
+TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an interface
+TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an entity
+TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted(void)
+{
+ return tud_audio_n_mounted(0);
+}
+
+// RX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+static inline uint16_t tud_audio_available(void)
+{
+ return tud_audio_n_available(0);
+}
+
+static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, buffer, bufsize);
+}
+
+static inline bool tud_audio_clear_ep_out_ff(void)
+{
+ return tud_audio_n_clear_ep_out_ff(0);
+}
+
+static inline tu_fifo_t* tud_audio_get_ep_out_ff(void)
+{
+ return tud_audio_n_get_ep_out_ff(0);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+
+static inline bool tud_audio_clear_rx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_clear_rx_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_available_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_available_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_read_support_ff(uint8_t ff_idx, void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read_support_ff(0, ff_idx, buffer, bufsize);
+}
+
+static inline tu_fifo_t* tud_audio_get_rx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_get_rx_support_ff(0, ff_idx);
+}
+
+#endif
+
+// TX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+static inline uint16_t tud_audio_write(const void * data, uint16_t len)
+{
+ return tud_audio_n_write(0, data, len);
+}
+
+static inline bool tud_audio_clear_ep_in_ff(void)
+{
+ return tud_audio_n_clear_ep_in_ff(0);
+}
+
+static inline tu_fifo_t* tud_audio_get_ep_in_ff(void)
+{
+ return tud_audio_n_get_ep_in_ff(0);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+static inline uint16_t tud_audio_flush_tx_support_ff(void)
+{
+ return tud_audio_n_flush_tx_support_ff(0);
+}
+
+static inline uint16_t tud_audio_clear_tx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_clear_tx_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_write_support_ff(uint8_t ff_idx, const void * data, uint16_t len)
+{
+ return tud_audio_n_write_support_ff(0, ff_idx, data, len);
+}
+
+static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_get_tx_support_ff(0, ff_idx);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
+{
+ return tud_audio_int_ctr_n_write(0, buffer, len);
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static inline bool tud_audio_fb_set(uint32_t feedback)
+{
+ return tud_audio_n_fb_set(0, feedback);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void audiod_init (void);
+void audiod_reset (uint8_t rhport);
+uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_AUDIO_DEVICE_H_ */
+
+/** @} */
+/** @} */
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
new file mode 100755
index 000000000..1d27ae7c5
--- /dev/null
+++ b/src/class/bth/bth_device.c
@@ -0,0 +1,254 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_BTH)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "bth_device.h"
+#include <device/usbd_pvt.h>
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_ev;
+ uint8_t ep_acl_in;
+ uint8_t ep_acl_out;
+ uint8_t ep_voice[2]; // Not used yet
+ uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
+
+ // Endpoint Transfer buffer
+ CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
+
+} btd_interface_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf;
+
+static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
+{
+ // skip if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(TUD_OPT_RHPORT, ep));
+
+ TU_ASSERT(usbd_edpt_xfer(TUD_OPT_RHPORT, ep, data, len));
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+bool tud_bt_event_send(void *event, uint16_t event_len)
+{
+ return bt_tx_data(_btd_itf.ep_ev, event, event_len);
+}
+
+bool tud_bt_acl_data_send(void *event, uint16_t event_len)
+{
+ return bt_tx_data(_btd_itf.ep_acl_in, event, event_len);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void btd_init(void)
+{
+ tu_memclr(&_btd_itf, sizeof(_btd_itf));
+}
+
+void btd_reset(uint8_t rhport)
+{
+ (void)rhport;
+}
+
+uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{
+ tusb_desc_endpoint_t const *desc_ep;
+ uint16_t drv_len = 0;
+ // Size of single alternative of ISO interface
+ const uint16_t iso_alt_itf_size = sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t);
+ // Size of hci interface
+ const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t);
+ // Ensure this is BT Primary Controller
+ TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
+ TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
+ TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0);
+
+ // Distinguish interface by number of endpoints, as both interface have same class, subclass and protocol
+ if (itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size)
+ {
+ _btd_itf.itf_num = itf_desc->bInterfaceNumber;
+
+ desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ _btd_itf.ep_ev = desc_ep->bEndpointAddress;
+
+ // Open endpoint pair
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
+ &_btd_itf.ep_acl_in), 0);
+
+ // Prepare for incoming data from host
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
+
+ drv_len = hci_itf_size;
+ }
+ else if (itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size)
+ {
+ uint8_t dir;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
+ TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
+ // Store endpoint size for alternative
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
+ // Store endpoint size for alternative
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+ drv_len += iso_alt_itf_size;
+
+ for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) {
+ // Make sure rest of alternatives matches
+ itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep);
+ if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE ||
+ TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass ||
+ TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass ||
+ TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol)
+ {
+ // Not an Iso interface instance
+ break;
+ }
+ TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ // Verify that alternative endpoint are same as first ones
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
+ _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+
+ desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
+ dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ // Verify that alternative endpoint are same as first ones
+ TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
+ _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
+ _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size;
+ drv_len += iso_alt_itf_size;
+ }
+ }
+
+ return drv_len;
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
+{
+ (void)rhport;
+
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+ request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
+ {
+ // HCI command packet addressing for single function Primary Controllers
+ TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
+ }
+ else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
+ {
+ if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex)
+ {
+ // TODO: Set interface it would involve changing size of endpoint size
+ }
+ else
+ {
+ // HCI command packet for Primary Controller function in a composite device
+ TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num);
+ }
+ }
+ else return false;
+
+ return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength);
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ // Handle class request only
+ TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+
+ if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength);
+ }
+
+ return true;
+}
+
+bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void)result;
+
+ // received new data from host
+ if (ep_addr == _btd_itf.ep_acl_out)
+ {
+ if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes);
+
+ // prepare for next data
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
+ }
+ else if (ep_addr == _btd_itf.ep_ev)
+ {
+ if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes);
+ }
+ else if (ep_addr == _btd_itf.ep_acl_in)
+ {
+ if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes);
+ }
+
+ return true;
+}
+
+#endif
diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h
new file mode 100755
index 000000000..1b90d0915
--- /dev/null
+++ b/src/class/bth/bth_device.h
@@ -0,0 +1,109 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_BTH_DEVICE_H_
+#define _TUSB_BTH_DEVICE_H_
+
+#include <common/tusb_common.h>
+#include <device/usbd.h>
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+#ifndef CFG_TUD_BTH_EVENT_EPSIZE
+#define CFG_TUD_BTH_EVENT_EPSIZE 16
+#endif
+#ifndef CFG_TUD_BTH_DATA_EPSIZE
+#define CFG_TUD_BTH_DATA_EPSIZE 64
+#endif
+
+typedef struct TU_ATTR_PACKED
+{
+ uint16_t op_code;
+ uint8_t param_length;
+ uint8_t param[255];
+} bt_hci_cmd_t;
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when HCI command was received over USB from Bluetooth host.
+// Detailed format is described in Bluetooth core specification Vol 2,
+// Part E, 5.4.1.
+// Length of the command is from 3 bytes (2 bytes for OpCode,
+// 1 byte for parameter total length) to 258.
+TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
+
+// Invoked when ACL data was received over USB from Bluetooth host.
+// Detailed format is described in Bluetooth core specification Vol 2,
+// Part E, 5.4.2.
+// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
+// and 16 bits for data total length) to endpoint size.
+TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
+
+// Called when event sent with tud_bt_event_send() was delivered to BT stack.
+// Controller can release/reuse buffer with Event packet at this point.
+TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes);
+
+// Called when ACL data that was sent with tud_bt_acl_data_send()
+// was delivered to BT stack.
+// Controller can release/reuse buffer with ACL packet at this point.
+TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes);
+
+// Bluetooth controller calls this function when it wants to send even packet
+// as described in Bluetooth core specification Vol 2, Part E, 5.4.4.
+// Event has at least 2 bytes, first is Event code second contains parameter
+// total length. Controller can release/reuse event memory after
+// tud_bt_event_sent_cb() is called.
+bool tud_bt_event_send(void *event, uint16_t event_len);
+
+// Bluetooth controller calls this to send ACL data packet
+// as described in Bluetooth core specification Vol 2, Part E, 5.4.2
+// Minimum length is 4 bytes, (12 bits for Handle, 4 bits for flags
+// and 16 bits for data total length). Upper limit is not limited
+// to endpoint size since buffer is allocate by controller
+// and must not be reused till tud_bt_acl_data_sent_cb() is called.
+bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void btd_init (void);
+void btd_reset (uint8_t rhport);
+uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool btd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
+bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_BTH_DEVICE_H_ */
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index f0488acbb..5df47f70b 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -63,7 +63,7 @@ typedef enum
CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2]
CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2]
CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2]
- CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2]
+ CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2]
CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2]
CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1]
CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1]
@@ -215,6 +215,10 @@ typedef enum
// Class Specific Functional Descriptor (Communication Interface)
//--------------------------------------------------------------------+
+// Start of all packed definitions for compiler without per-type packed
+TU_ATTR_PACKED_BEGIN
+TU_ATTR_BIT_FIELD_ORDER_BEGIN
+
/// Header Functional Descriptor (Communication Interface)
typedef struct TU_ATTR_PACKED
{
@@ -235,7 +239,7 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_union_t;
#define cdc_desc_func_union_n_t(no_slave)\
- struct TU_ATTR_PACKED { \
+ struct TU_ATTR_PACKED { \
uint8_t bLength ;\
uint8_t bDescriptorType ;\
uint8_t bDescriptorSubType ;\
@@ -254,7 +258,7 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_country_selection_t;
#define cdc_desc_func_country_selection_n_t(no_country) \
- struct TU_ATTR_PACKED {\
+ struct TU_ATTR_PACKED { \
uint8_t bLength ;\
uint8_t bDescriptorType ;\
uint8_t bDescriptorSubType ;\
@@ -277,26 +281,25 @@ typedef struct TU_ATTR_PACKED
struct {
uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface.
uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself.
- uint8_t : 0;
+ uint8_t TU_RESERVED : 6;
} bmCapabilities;
uint8_t bDataInterface;
}cdc_desc_func_call_management_t;
-
typedef struct TU_ATTR_PACKED
{
uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature.
uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State.
uint8_t support_send_break : 1; ///< Device supports the request Send_Break
uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection.
- uint8_t : 0;
+ uint8_t TU_RESERVED : 4;
}cdc_acm_capability_t;
TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler");
-/// \brief Abstract Control Management Functional Descriptor
-/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
+/// Abstract Control Management Functional Descriptor
+/// This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
@@ -316,7 +319,7 @@ typedef struct TU_ATTR_PACKED
uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit.
uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State.
uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time.
- uint8_t : 0;
+ uint8_t TU_RESERVED : 5;
} bmCapabilities;
}cdc_desc_func_direct_line_management_t;
@@ -344,7 +347,7 @@ typedef struct TU_ATTR_PACKED
uint8_t simple_mode : 1;
uint8_t standalone_mode : 1;
uint8_t computer_centric_mode : 1;
- uint8_t : 0;
+ uint8_t TU_RESERVED : 5;
} bmCapabilities;
}cdc_desc_func_telephone_operational_modes_t;
@@ -363,10 +366,11 @@ typedef struct TU_ATTR_PACKED
uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns.
uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line.
uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification
- uint32_t : 0;
+ uint32_t TU_RESERVED : 26;
} bmCapabilities;
}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
+// TODO remove
static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
{
return p_desc[2];
@@ -394,7 +398,8 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
-/** @} */
+TU_ATTR_PACKED_END // End of all packed definitions
+TU_ATTR_BIT_FIELD_ORDER_END
#ifdef __cplusplus
}
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index c0de0cadd..e622bd616 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -28,12 +28,19 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC)
-#include "cdc_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "cdc_device.h"
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+enum
+{
+ BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
+};
+
typedef struct
{
uint8_t itf_num;
@@ -61,8 +68,8 @@ typedef struct
#endif
// Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
}cdcd_interface_t;
@@ -73,18 +80,30 @@ typedef struct
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
-static void _prep_out_transaction (uint8_t itf)
+static void _prep_out_transaction (cdcd_interface_t* p_cdc)
{
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
-
- // skip if previous transfer not complete
- if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
+ uint8_t const rhport = TUD_OPT_RHPORT;
+ uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
- uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
- if ( max_read >= CFG_TUD_CDC_EPSIZE )
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= sizeof(p_cdc->epout_buf), );
+
+ // claim endpoint
+ TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), );
+
+ // fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ if ( available >= sizeof(p_cdc->epout_buf) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
+ }else
{
- usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, CFG_TUD_CDC_EPSIZE);
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_cdc->ep_out);
}
}
@@ -123,20 +142,22 @@ uint32_t tud_cdc_n_available(uint8_t itf)
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
{
- uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize);
- _prep_out_transaction(itf);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, bufsize);
+ _prep_out_transaction(p_cdc);
return num_read;
}
-bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr)
{
- return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr);
+ return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
}
void tud_cdc_n_read_flush (uint8_t itf)
{
- tu_fifo_clear(&_cdcd_itf[itf].rx_ff);
- _prep_out_transaction(itf);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ tu_fifo_clear(&p_cdc->rx_ff);
+ _prep_out_transaction(p_cdc);
}
//--------------------------------------------------------------------+
@@ -144,32 +165,47 @@ void tud_cdc_n_read_flush (uint8_t itf)
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
{
- uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, bufsize);
-#if 0 // TODO issue with circuitpython's REPL
- // flush if queue more than endpoint size
- if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE )
+ // flush if queue more than packet size
+ if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE )
{
tud_cdc_n_write_flush(itf);
}
-#endif
return ret;
}
-bool tud_cdc_n_write_flush (uint8_t itf)
+uint32_t tud_cdc_n_write_flush (uint8_t itf)
{
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete
- uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, CFG_TUD_CDC_EPSIZE);
+ // Skip if usb is not ready yet
+ TU_VERIFY( tud_ready(), 0 );
+
+ // No data to send
+ if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
+
+ // Claim the endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+
+ // Pull data from FIFO
+ uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+
if ( count )
{
- TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) );
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, 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(rhport, p_cdc->ep_in);
+ return 0;
}
-
- return true;
}
uint32_t tud_cdc_n_write_available (uint8_t itf)
@@ -177,6 +213,10 @@ uint32_t tud_cdc_n_write_available (uint8_t itf)
return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
}
+bool tud_cdc_n_write_clear (uint8_t itf)
+{
+ return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
+}
//--------------------------------------------------------------------+
// USBD Driver API
@@ -192,18 +232,22 @@ void cdcd_init(void)
p_cdc->wanted_char = -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
- p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.bit_rate = 115200;
p_cdc->line_coding.stop_bits = 0;
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
- // config fifo
- tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE, 1, false);
- tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, 1, false);
+ // 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);
+
+ // Config TX fifo as overwritable at initialization and will be changed to non-overwritable
+ // if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal.
+ // In this way, the most current data is prioritized.
+ tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
- tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
#endif
}
}
@@ -214,25 +258,24 @@ void cdcd_reset(uint8_t rhport)
for(uint8_t i=0; i<CFG_TUD_CDC; i++)
{
- tu_memclr(&_cdcd_itf[i], ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&_cdcd_itf[i].rx_ff);
- tu_fifo_clear(&_cdcd_itf[i].tx_ff);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[i];
+
+ tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
+ tu_fifo_clear(&p_cdc->rx_ff);
+ tu_fifo_clear(&p_cdc->tx_ff);
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
}
}
-bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
+uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// Only support ACM subclass
- TU_ASSERT ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
-
- // Only support AT commands, no protocol and vendor specific commands.
- TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
- itf_desc->bInterfaceProtocol == 0xff);
+ 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 = NULL;
- uint8_t cdc_id;
- for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
+ for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
{
if ( _cdcd_itf[cdc_id].ep_in == 0 )
{
@@ -240,83 +283,57 @@ bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
break;
}
}
- TU_ASSERT(p_cdc);
+ TU_ASSERT(p_cdc, 0);
//------------- Control Interface -------------//
p_cdc->itf_num = itf_desc->bInterfaceNumber;
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const * p_desc = tu_desc_next( itf_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
// Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
- (*p_length) += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
- // notification endpoint if any
- TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
+ // notification endpoint
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
+ 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);
+ drv_len += tu_desc_len(p_desc);
+ 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) );
+ TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
- (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+ drv_len += 2*sizeof(tusb_desc_endpoint_t);
}
// Prepare for incoming data
- _prep_out_transaction(cdc_id);
-
- return true;
-}
-
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
-
- //------------- Class Specific Request -------------//
- TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
-
- // Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if ( p_cdc->itf_num == request->wIndex ) break;
- }
+ _prep_out_transaction(p_cdc);
- // Invoke callback
- if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest )
- {
- if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
- }
-
- return true;
+ return drv_len;
}
-// Handle class control request
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request)
+bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
@@ -335,25 +352,61 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request
switch ( request->bRequest )
{
case CDC_REQUEST_SET_LINE_CODING:
- tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ TU_LOG2(" Set Line Coding\r\n");
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
+ else if ( stage == CONTROL_STAGE_ACK)
+ {
+ if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ }
break;
case CDC_REQUEST_GET_LINE_CODING:
- tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ TU_LOG2(" Get Line Coding\r\n");
+ tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+ }
break;
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- // CDC PSTN v1.2 section 6.3.12
- // Bit 0: Indicates if DTE is present or not.
- // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
- // Bit 1: Carrier control for half-duplex modems.
- // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
- p_cdc->line_state = (uint8_t) request->wValue;
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ // CDC PSTN v1.2 section 6.3.12
+ // Bit 0: Indicates if DTE is present or not.
+ // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
+ // Bit 1: Carrier control for half-duplex modems.
+ // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
+ bool const dtr = tu_bit_test(request->wValue, 0);
+ bool const rts = tu_bit_test(request->wValue, 1);
- tud_control_status(rhport, request);
+ p_cdc->line_state = (uint8_t) request->wValue;
+
+ // Disable fifo overwriting if DTR bit is set
+ tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
- // Invoke callback
- if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, tu_bit_test(request->wValue, 0), tu_bit_test(request->wValue, 1));
+ TU_LOG2(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+
+ // Invoke callback
+ if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
+ }
+ break;
+ case CDC_REQUEST_SEND_BREAK:
+ if (stage == CONTROL_STAGE_SETUP)
+ {
+ tud_control_status(rhport, request);
+ }
+ else if (stage == CONTROL_STAGE_ACK)
+ {
+ TU_LOG2(" Send Break\r\n");
+ if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
+ }
break;
default: return false; // stall unsupported request
@@ -364,48 +417,64 @@ bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
- (void) rhport;
(void) result;
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
+ uint8_t itf;
+ cdcd_interface_t* p_cdc;
// Identify which interface to use
- for ( ; ; itf++, p_cdc++)
+ for (itf = 0; itf < CFG_TUD_CDC; itf++)
{
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
+ p_cdc = &_cdcd_itf[itf];
if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
}
+ TU_ASSERT(itf < CFG_TUD_CDC);
// Received new data
if ( ep_addr == p_cdc->ep_out )
{
- for(uint32_t i=0; i<xferred_bytes; i++)
+ tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, xferred_bytes);
+
+ // Check for wanted char and invoke callback if needed
+ if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) )
{
- tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]);
-
- // Check for wanted char and invoke callback if needed
- if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) )
+ for ( uint32_t i = 0; i < xferred_bytes; i++ )
{
- tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) )
+ {
+ tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
+ }
}
}
-
+
// invoke receive callback (if there is still data)
- if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
-
+ if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
+
// prepare for OUT transaction
- _prep_out_transaction(itf);
+ _prep_out_transaction(p_cdc);
}
+
+ // Data sent to host, we continue to fetch from tx fifo to send.
+ // 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
+ if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf);
- // Data sent to host, we could continue to fetch data tx fifo to send.
- // But it will cause incorrect baudrate set in line coding.
- // Though maybe the baudrate is not really important !!!
-// if ( ep_addr == p_cdc->ep_in )
-// {
-//
-// }
+ 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) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
+ }
+ }
+ }
+ }
// nothing to do with notif endpoint for now
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 969ec7f99..7ff757add 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -28,14 +28,18 @@
#define _TUSB_CDC_DEVICE_H_
#include "common/tusb_common.h"
-#include "device/usbd.h"
#include "cdc.h"
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
-#ifndef CFG_TUD_CDC_EPSIZE
-#define CFG_TUD_CDC_EPSIZE 64
+#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
+#endif
+
+#ifndef CFG_TUD_CDC_EP_BUFSIZE
+ #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifdef __cplusplus
@@ -48,28 +52,60 @@
* @{ */
//--------------------------------------------------------------------+
-// Application API (Multiple Interfaces)
+// Application API (Multiple Ports)
// CFG_TUD_CDC > 1
//--------------------------------------------------------------------+
+
+// Check if 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)
uint8_t tud_cdc_n_get_line_state (uint8_t itf);
+
+// Get current line encoding: bit rate, stop bits parity etc ..
void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
+
+// Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving
void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
+// Get the number of bytes available for reading
uint32_t tud_cdc_n_available (uint8_t itf);
+
+// Read received bytes
uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
+
+// Read a byte, return -1 if there is none
+static inline
+int32_t tud_cdc_n_read_char (uint8_t itf);
+
+// Clear the received FIFO
void tud_cdc_n_read_flush (uint8_t itf);
-bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8);
-static inline int32_t tud_cdc_n_read_char (uint8_t itf);
+// Get a byte from FIFO at the specified position without removing it
+bool tud_cdc_n_peek (uint8_t itf, uint8_t* u8);
+
+// Write bytes to TX FIFO, data may remain in the FIFO for a while
uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
-bool tud_cdc_n_write_flush (uint8_t itf);
+
+// Write a byte
+static inline
+uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
+
+// Write a null-terminated string
+static inline
+uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
+
+// Force sending data if possible, return number of forced bytes
+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);
-static inline uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
+
+// Clear the transmit FIFO
+bool tud_cdc_n_write_clear (uint8_t itf);
//--------------------------------------------------------------------+
-// Application API (Interface0)
+// Application API (Single Port)
//--------------------------------------------------------------------+
static inline bool tud_cdc_connected (void);
static inline uint8_t tud_cdc_get_line_state (void);
@@ -80,13 +116,14 @@ static inline uint32_t tud_cdc_available (void);
static inline int32_t tud_cdc_read_char (void);
static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize);
static inline void tud_cdc_read_flush (void);
-static inline bool tud_cdc_peek (int pos, uint8_t* u8);
+static inline bool tud_cdc_peek (uint8_t* u8);
static inline uint32_t tud_cdc_write_char (char ch);
static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize);
static inline uint32_t tud_cdc_write_str (char const* str);
-static inline bool tud_cdc_write_flush (void);
+static inline uint32_t tud_cdc_write_flush (void);
static inline uint32_t tud_cdc_write_available (void);
+static inline bool tud_cdc_write_clear (void);
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
@@ -98,12 +135,18 @@ TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
// Invoked when received `wanted_char`
TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+// Invoked when space becomes available in TX buffer
+TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf);
+
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
// Invoked when line coding is change via SET_LINE_CODING
TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
+// Invoked when received send break
+TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
+
//--------------------------------------------------------------------+
// Inline Functions
//--------------------------------------------------------------------+
@@ -163,9 +206,9 @@ static inline void tud_cdc_read_flush (void)
tud_cdc_n_read_flush(0);
}
-static inline bool tud_cdc_peek (int pos, uint8_t* u8)
+static inline bool tud_cdc_peek (uint8_t* u8)
{
- return tud_cdc_n_peek(0, pos, u8);
+ return tud_cdc_n_peek(0, u8);
}
static inline uint32_t tud_cdc_write_char (char ch)
@@ -183,7 +226,7 @@ static inline uint32_t tud_cdc_write_str (char const* str)
return tud_cdc_n_write_str(0, str);
}
-static inline bool tud_cdc_write_flush (void)
+static inline uint32_t tud_cdc_write_flush (void)
{
return tud_cdc_n_write_flush(0);
}
@@ -193,18 +236,22 @@ static inline uint32_t tud_cdc_write_available(void)
return tud_cdc_n_write_available(0);
}
+static inline bool tud_cdc_write_clear(void)
+{
+ return tud_cdc_n_write_clear(0);
+}
+
/** @} */
/** @} */
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
-void cdcd_init (void);
-void cdcd_reset (uint8_t rhport);
-bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
-bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void cdcd_init (void);
+void cdcd_reset (uint8_t rhport);
+uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 595e7fd62..35b8de3b1 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -28,7 +28,9 @@
#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC)
-#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "host/usbh_classdriver.h"
+
#include "cdc_host.h"
//--------------------------------------------------------------------+
@@ -51,9 +53,14 @@ typedef struct {
//--------------------------------------------------------------------+
static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX];
+static inline cdch_data_t* get_itf(uint8_t dev_addr)
+{
+ return &cdch_data[dev_addr-1];
+}
+
bool tuh_cdc_mounted(uint8_t dev_addr)
{
- cdch_data_t* cdc = &cdch_data[dev_addr-1];
+ cdch_data_t* cdc = get_itf(dev_addr);
return cdc->ep_in && cdc->ep_out;
}
@@ -61,18 +68,18 @@ bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid)
{
if ( !tuh_cdc_mounted(dev_addr) ) return false;
- cdch_data_t const * p_cdc = &cdch_data[dev_addr-1];
+ cdch_data_t const * p_cdc = get_itf(dev_addr);
switch (pipeid)
{
case CDC_PIPE_NOTIFICATION:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_notif );
+ return usbh_edpt_busy(dev_addr, p_cdc->ep_notif );
case CDC_PIPE_DATA_IN:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_in );
+ return usbh_edpt_busy(dev_addr, p_cdc->ep_in );
case CDC_PIPE_DATA_OUT:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_out );
+ return usbh_edpt_busy(dev_addr, p_cdc->ep_out );
default:
return false;
@@ -86,30 +93,52 @@ bool tuh_cdc_serial_is_mounted(uint8_t dev_addr)
{
// TODO consider all AT Command as serial candidate
return tuh_cdc_mounted(dev_addr) &&
- (CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) &&
(cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
}
bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
{
+ (void) is_notify;
TU_VERIFY( tuh_cdc_mounted(dev_addr) );
TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA);
uint8_t const ep_out = cdch_data[dev_addr-1].ep_out;
- if ( hcd_edpt_busy(dev_addr, ep_out) ) return false;
+ if ( usbh_edpt_busy(dev_addr, ep_out) ) return false;
- return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify);
+ return usbh_edpt_xfer(dev_addr, ep_out, (void *) p_data, length);
}
bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
{
+ (void) is_notify;
TU_VERIFY( tuh_cdc_mounted(dev_addr) );
TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA);
uint8_t const ep_in = cdch_data[dev_addr-1].ep_in;
- if ( hcd_edpt_busy(dev_addr, ep_in) ) return false;
+ if ( usbh_edpt_busy(dev_addr, ep_in) ) return false;
+
+ return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, length);
+}
+
+bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb)
+{
+ cdch_data_t const * p_cdc = get_itf(dev_addr);
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
+ .wValue = (rts ? 2 : 0) | (dtr ? 1 : 0),
+ .wIndex = p_cdc->itf_num,
+ .wLength = 0
+ };
- return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify);
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, complete_cb) );
+ return true;
}
//--------------------------------------------------------------------+
@@ -120,29 +149,27 @@ void cdch_init(void)
tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
}
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
{
- // Only support ACM
- TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
-
- // Only support AT commands, no protocol and vendor specific commands.
- TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
- 0xff == itf_desc->bInterfaceProtocol);
+ (void) max_len;
- uint8_t const * p_desc;
- cdch_data_t * p_cdc;
+ // Only support ACM subclass
+ // Protocol 0xFF can be RNDIS device for windows XP
+ TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
+ 0xFF != itf_desc->bInterfaceProtocol);
- p_desc = tu_desc_next(itf_desc);
- p_cdc = &cdch_data[dev_addr-1];
+ cdch_data_t * p_cdc = get_itf(dev_addr);
- p_cdc->itf_num = itf_desc->bInterfaceNumber;
- p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
+ p_cdc->itf_num = itf_desc->bInterfaceNumber;
+ p_cdc->itf_protocol = itf_desc->bInterfaceProtocol;
//------------- Communication Interface -------------//
- (*p_length) = sizeof(tusb_desc_interface_t);
+ uint16_t drv_len = tu_desc_len(itf_desc);
+ uint8_t const * p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
- while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] )
+ while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
{
@@ -150,75 +177,70 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
}
- (*p_length) += p_desc[DESC_OFFSET_LEN];
+ drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
- if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
// notification endpoint
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) );
- p_cdc->ep_notif = ep_desc->bEndpointAddress;
+ TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
- (*p_length) += p_desc[DESC_OFFSET_LEN];
+ drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
+ if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
(TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
{
- (*p_length) += p_desc[DESC_OFFSET_LEN];
+ // next to endpoint descriptor
+ drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
// data endpoints expected to be in pairs
for(uint32_t i=0; i<2; i++)
{
- tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
- TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
- TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep));
- if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
+ if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
{
- p_cdc->ep_in = ep_desc->bEndpointAddress;
+ p_cdc->ep_in = desc_ep->bEndpointAddress;
}else
{
- p_cdc->ep_out = ep_desc->bEndpointAddress;
+ p_cdc->ep_out = desc_ep->bEndpointAddress;
}
- (*p_length) += p_desc[DESC_OFFSET_LEN];
+ drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next( p_desc );
}
}
- // FIXME move to seperate API : connect
- tusb_control_request_t request =
- {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
- .wValue = 0x03, // dtr on, cst on
- .wIndex = p_cdc->itf_num,
- .wLength = 0
- };
-
- TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) );
+ return true;
+}
+bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ (void) dev_addr; (void) itf_num;
return true;
}
-void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
(void) ep_addr;
tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes );
+ return true;
}
void cdch_close(uint8_t dev_addr)
{
- cdch_data_t * p_cdc = &cdch_data[dev_addr-1];
+ cdch_data_t * p_cdc = get_itf(dev_addr);
tu_memclr(p_cdc, sizeof(cdch_data_t));
}
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 306420ce4..0d435138b 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -27,8 +27,6 @@
#ifndef _TUSB_CDC_HOST_H_
#define _TUSB_CDC_HOST_H_
-#include "common/tusb_common.h"
-#include "host/usbh.h"
#include "cdc.h"
#ifdef __cplusplus
@@ -44,6 +42,18 @@
* \defgroup CDC_Serial_Host Host
* @{ */
+bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_complete_cb_t complete_cb);
+
+static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb)
+{
+ return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb);
+}
+
+static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_control_complete_cb_t complete_cb)
+{
+ return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb);
+}
+
/** \brief Check if device support CDC Serial interface or not
* \param[in] dev_addr device address
* \retval true if device supports
@@ -111,10 +121,11 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void cdch_init(void);
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void cdch_close(uint8_t dev_addr);
+void cdch_init (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);
#ifdef __cplusplus
}
diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c
index 767b917a1..42b9993bc 100644
--- a/src/class/cdc/cdc_rndis_host.c
+++ b/src/class/cdc/cdc_rndis_host.c
@@ -239,7 +239,7 @@ static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_da
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
//------------- waiting for Response Available notification -------------//
- (void) hcd_pipe_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8, true);
+ (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);
diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h
new file mode 100644
index 000000000..114c827b8
--- /dev/null
+++ b/src/class/dfu/dfu.h
@@ -0,0 +1,119 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 XMOS LIMITED
+ *
+ * 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_DFU_H_
+#define _TUSB_DFU_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Common Definitions
+//--------------------------------------------------------------------+
+
+// DFU Protocol
+typedef enum
+{
+ DFU_PROTOCOL_RT = 0x01,
+ DFU_PROTOCOL_DFU = 0x02,
+} dfu_protocol_type_t;
+
+// DFU Descriptor Type
+typedef enum
+{
+ DFU_DESC_FUNCTIONAL = 0x21,
+} dfu_descriptor_type_t;
+
+// DFU Requests
+typedef enum {
+ DFU_REQUEST_DETACH = 0,
+ DFU_REQUEST_DNLOAD = 1,
+ DFU_REQUEST_UPLOAD = 2,
+ DFU_REQUEST_GETSTATUS = 3,
+ DFU_REQUEST_CLRSTATUS = 4,
+ DFU_REQUEST_GETSTATE = 5,
+ DFU_REQUEST_ABORT = 6,
+} dfu_requests_t;
+
+// DFU States
+typedef enum {
+ APP_IDLE = 0,
+ APP_DETACH = 1,
+ DFU_IDLE = 2,
+ DFU_DNLOAD_SYNC = 3,
+ DFU_DNBUSY = 4,
+ DFU_DNLOAD_IDLE = 5,
+ DFU_MANIFEST_SYNC = 6,
+ DFU_MANIFEST = 7,
+ DFU_MANIFEST_WAIT_RESET = 8,
+ DFU_UPLOAD_IDLE = 9,
+ DFU_ERROR = 10,
+} dfu_state_t;
+
+// DFU Status
+typedef enum {
+ DFU_STATUS_OK = 0x00,
+ DFU_STATUS_ERR_TARGET = 0x01,
+ DFU_STATUS_ERR_FILE = 0x02,
+ DFU_STATUS_ERR_WRITE = 0x03,
+ DFU_STATUS_ERR_ERASE = 0x04,
+ DFU_STATUS_ERR_CHECK_ERASED = 0x05,
+ DFU_STATUS_ERR_PROG = 0x06,
+ DFU_STATUS_ERR_VERIFY = 0x07,
+ DFU_STATUS_ERR_ADDRESS = 0x08,
+ DFU_STATUS_ERR_NOTDONE = 0x09,
+ DFU_STATUS_ERR_FIRMWARE = 0x0A,
+ DFU_STATUS_ERR_VENDOR = 0x0B,
+ DFU_STATUS_ERR_USBR = 0x0C,
+ DFU_STATUS_ERR_POR = 0x0D,
+ DFU_STATUS_ERR_UNKNOWN = 0x0E,
+ DFU_STATUS_ERR_STALLEDPKT = 0x0F,
+} dfu_status_t;
+
+#define DFU_ATTR_CAN_DOWNLOAD (1u << 0)
+#define DFU_ATTR_CAN_UPLOAD (1u << 1)
+#define DFU_ATTR_MANIFESTATION_TOLERANT (1u << 2)
+#define DFU_ATTR_WILL_DETACH (1u << 3)
+
+// DFU Status Request Payload
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bStatus;
+ uint8_t bwPollTimeout[3];
+ uint8_t bState;
+ uint8_t iString;
+} dfu_status_response_t;
+
+TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DFU_H_ */
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
new file mode 100644
index 000000000..28abf6e83
--- /dev/null
+++ b/src/class/dfu/dfu_device.c
@@ -0,0 +1,458 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 XMOS LIMITED
+ *
+ * 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 (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU)
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "dfu_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+typedef struct
+{
+ uint8_t attrs;
+ uint8_t alt;
+
+ dfu_state_t state;
+ dfu_status_t status;
+
+ bool flashing_in_progress;
+ uint16_t block;
+ uint16_t length;
+
+ CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
+} dfu_state_ctx_t;
+
+// Only a single dfu state is allowed
+CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_ctx;
+
+static void reset_state(void)
+{
+ _dfu_ctx.state = DFU_IDLE;
+ _dfu_ctx.status = DFU_STATUS_OK;
+ _dfu_ctx.flashing_in_progress = false;
+}
+
+static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
+static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+
+//--------------------------------------------------------------------+
+// Debug
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= 2
+
+static tu_lookup_entry_t const _dfu_request_lookup[] =
+{
+ { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
+ { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
+ { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
+ { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
+ { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
+ { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
+ { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
+};
+
+static tu_lookup_table_t const _dfu_request_table =
+{
+ .count = TU_ARRAY_SIZE(_dfu_request_lookup),
+ .items = _dfu_request_lookup
+};
+
+static tu_lookup_entry_t const _dfu_state_lookup[] =
+{
+ { .key = APP_IDLE , .data = "APP_IDLE" },
+ { .key = APP_DETACH , .data = "APP_DETACH" },
+ { .key = DFU_IDLE , .data = "IDLE" },
+ { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
+ { .key = DFU_DNBUSY , .data = "DNBUSY" },
+ { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
+ { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
+ { .key = DFU_MANIFEST , .data = "MANIFEST" },
+ { .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
+ { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
+ { .key = DFU_ERROR , .data = "ERROR" },
+};
+
+static tu_lookup_table_t const _dfu_state_table =
+{
+ .count = TU_ARRAY_SIZE(_dfu_state_lookup),
+ .items = _dfu_state_lookup
+};
+
+static tu_lookup_entry_t const _dfu_status_lookup[] =
+{
+ { .key = DFU_STATUS_OK , .data = "OK" },
+ { .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
+ { .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
+ { .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" },
+ { .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" },
+ { .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" },
+ { .key = DFU_STATUS_ERR_PROG , .data = "errPROG" },
+ { .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" },
+ { .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" },
+ { .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" },
+ { .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" },
+ { .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" },
+ { .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" },
+ { .key = DFU_STATUS_ERR_POR , .data = "errPOR" },
+ { .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" },
+ { .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
+};
+
+static tu_lookup_table_t const _dfu_status_table =
+{
+ .count = TU_ARRAY_SIZE(_dfu_status_lookup),
+ .items = _dfu_status_lookup
+};
+
+#endif
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void dfu_moded_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ _dfu_ctx.attrs = 0;
+ _dfu_ctx.alt = 0;
+
+ reset_state();
+}
+
+void dfu_moded_init(void)
+{
+ dfu_moded_reset(0);
+}
+
+uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) rhport;
+
+ //------------- Interface (with Alt) descriptor -------------//
+ uint8_t const itf_num = itf_desc->bInterfaceNumber;
+ uint8_t alt_count = 0;
+
+ uint16_t drv_len = 0;
+ while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
+ {
+ TU_ASSERT(max_len > drv_len, 0);
+
+ // Alternate must have the same interface number
+ TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0);
+
+ // Alt should increase by one every time
+ TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0);
+ alt_count++;
+
+ drv_len += tu_desc_len(itf_desc);
+ itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
+ }
+
+ //------------- DFU Functional descriptor -------------//
+ tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
+ TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
+ drv_len += sizeof(tusb_desc_dfu_functional_t);
+
+ _dfu_ctx.attrs = func_desc->bAttributes;
+
+ // CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
+ uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
+ TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
+
+ return drv_len;
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
+
+ TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
+
+ if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
+ {
+ // Standard request include GET/SET_INTERFACE
+ switch ( request->bRequest )
+ {
+ case TUSB_REQ_SET_INTERFACE:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // Switch Alt interface and reset state machine
+ _dfu_ctx.alt = (uint8_t) request->wValue;
+ reset_state();
+ return tud_control_status(rhport, request);
+ }
+ break;
+
+ case TUSB_REQ_GET_INTERFACE:
+ if(stage == CONTROL_STAGE_SETUP)
+ {
+ return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
+ }
+ break;
+
+ // unsupported request
+ default: return false;
+ }
+ }
+ else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
+ {
+ TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
+
+ // Class request
+ switch ( request->bRequest )
+ {
+ case DFU_REQUEST_DETACH:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ tud_control_status(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
+ }
+ break;
+
+ case DFU_REQUEST_CLRSTATUS:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ reset_state();
+ tud_control_status(rhport, request);
+ }
+ break;
+
+ case DFU_REQUEST_GETSTATE:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
+ }
+ break;
+
+ case DFU_REQUEST_ABORT:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ reset_state();
+ tud_control_status(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
+ }
+ break;
+
+ case DFU_REQUEST_UPLOAD:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
+ TU_VERIFY(tud_dfu_upload_cb);
+ TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
+
+ uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
+
+ return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
+ }
+ break;
+
+ case DFU_REQUEST_DNLOAD:
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
+ TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
+ TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
+
+ // set to true for both download and manifest
+ _dfu_ctx.flashing_in_progress = true;
+
+ // save block and length for flashing
+ _dfu_ctx.block = request->wValue;
+ _dfu_ctx.length = request->wLength;
+
+ if ( request->wLength )
+ {
+ // Download with payload -> transition to DOWNLOAD SYNC
+ _dfu_ctx.state = DFU_DNLOAD_SYNC;
+ return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
+ }
+ else
+ {
+ // Download is complete -> transition to MANIFEST SYNC
+ _dfu_ctx.state = DFU_MANIFEST_SYNC;
+ return tud_control_status(rhport, request);
+ }
+ }
+ break;
+
+ case DFU_REQUEST_GETSTATUS:
+ switch ( _dfu_ctx.state )
+ {
+ case DFU_DNLOAD_SYNC:
+ return process_download_get_status(rhport, stage, request);
+ break;
+
+ case DFU_MANIFEST_SYNC:
+ return process_manifest_get_status(rhport, stage, request);
+ break;
+
+ default:
+ if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ break;
+ }
+ break;
+
+ default: return false; // stall unsupported request
+ }
+ }else
+ {
+ return false; // unsupported request
+ }
+
+ return true;
+}
+
+void tud_dfu_finish_flashing(uint8_t status)
+{
+ _dfu_ctx.flashing_in_progress = false;
+
+ if ( status == DFU_STATUS_OK )
+ {
+ if (_dfu_ctx.state == DFU_DNBUSY)
+ {
+ _dfu_ctx.state = DFU_DNLOAD_SYNC;
+ }
+ else if (_dfu_ctx.state == DFU_MANIFEST)
+ {
+ _dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
+ ? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
+ }
+ }
+ else
+ {
+ // failed while flashing, move to dfuError
+ _dfu_ctx.state = DFU_ERROR;
+ _dfu_ctx.status = (dfu_status_t)status;
+ }
+}
+
+static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // only transition to next state on CONTROL_STAGE_ACK
+ dfu_state_t next_state;
+ uint32_t timeout;
+
+ if ( _dfu_ctx.flashing_in_progress )
+ {
+ next_state = DFU_DNBUSY;
+ timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
+ }
+ else
+ {
+ next_state = DFU_DNLOAD_IDLE;
+ timeout = 0;
+ }
+
+ return reply_getstatus(rhport, request, next_state, _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_ctx.transfer_buf, _dfu_ctx.length);
+ }else
+ {
+ _dfu_ctx.state = DFU_DNLOAD_IDLE;
+ }
+ }
+
+ return true;
+}
+
+static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // only transition to next state on CONTROL_STAGE_ACK
+ dfu_state_t next_state;
+ uint32_t timeout;
+
+ if ( _dfu_ctx.flashing_in_progress )
+ {
+ next_state = DFU_MANIFEST;
+ timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
+ }
+ else
+ {
+ next_state = DFU_IDLE;
+ timeout = 0;
+ }
+
+ return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ if ( _dfu_ctx.flashing_in_progress )
+ {
+ _dfu_ctx.state = DFU_MANIFEST;
+ tud_dfu_manifest_cb(_dfu_ctx.alt);
+ }
+ else
+ {
+ _dfu_ctx.state = DFU_IDLE;
+ }
+ }
+
+ return true;
+}
+
+static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
+{
+ dfu_status_response_t resp;
+ resp.bStatus = (uint8_t) status;
+ resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
+ resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
+ resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
+ resp.bState = (uint8_t) state;
+ resp.iString = 0;
+
+ return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
+}
+
+#endif
diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h
new file mode 100644
index 000000000..fecf8596f
--- /dev/null
+++ b/src/class/dfu/dfu_device.h
@@ -0,0 +1,98 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 XMOS LIMITED
+ *
+ * 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_DFU_DEVICE_H_
+#define _TUSB_DFU_DEVICE_H_
+
+#include "dfu.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Default Configure & Validation
+//--------------------------------------------------------------------+
+
+#if !defined(CFG_TUD_DFU_XFER_BUFSIZE)
+ #error "CFG_TUD_DFU_XFER_BUFSIZE must be defined, it has to be set to the buffer size used in TUD_DFU_DESCRIPTOR"
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Must be called when the application is done with flashing started by
+// tud_dfu_download_cb() and tud_dfu_manifest_cb().
+// status is DFU_STATUS_OK if successful, any other error status will cause state to enter dfuError
+void tud_dfu_finish_flashing(uint8_t status);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
+
+// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
+// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
+// During this period, USB host won't try to communicate with us.
+uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state);
+
+// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
+// This callback could be returned before flashing op is complete (async).
+// Once finished flashing, application must call tud_dfu_finish_flashing()
+void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length);
+
+// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
+// Application can do checksum, or actual flashing if buffered entire image previously.
+// Once finished flashing, application must call tud_dfu_finish_flashing()
+void tud_dfu_manifest_cb(uint8_t alt);
+
+// Invoked when received DFU_UPLOAD request
+// Application must populate data with up to length bytes and
+// Return the number of written bytes
+TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
+
+// Invoked when a DFU_DETACH request is received
+TU_ATTR_WEAK void tud_dfu_detach_cb(void);
+
+// Invoked when the Host has terminated a download or upload transfer
+TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+void dfu_moded_init(void);
+void dfu_moded_reset(uint8_t rhport);
+uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_DFU_MODE_DEVICE_H_ */
diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c
index ad1871dad..afee2aa1f 100644
--- a/src/class/dfu/dfu_rt_device.c
+++ b/src/class/dfu/dfu_rt_device.c
@@ -26,23 +26,20 @@
#include "tusb_option.h"
-#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RT)
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME)
-#include "dfu_rt_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "dfu_rt_device.h"
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef enum {
- DFU_REQUEST_DETACH = 0,
- DFU_REQUEST_DNLOAD = 1,
- DFU_REQUEST_UPLOAD = 2,
- DFU_REQUEST_GETSTATUS = 3,
- DFU_REQUEST_CLRSTATUS = 4,
- DFU_REQUEST_GETSTATE = 5,
- DFU_REQUEST_ABORT = 6,
-} dfu_requests_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// USBD Driver API
@@ -53,63 +50,78 @@ void dfu_rtd_init(void)
void dfu_rtd_reset(uint8_t rhport)
{
- (void) rhport;
+ (void) rhport;
}
-bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
+uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void) rhport;
+ (void) max_len;
// Ensure this is DFU Runtime
- TU_ASSERT(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS);
- TU_ASSERT(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT);
+ TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) &&
+ (itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0);
uint8_t const * p_desc = tu_desc_next( itf_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
{
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- return true;
+ return drv_len;
}
-bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
- (void) rhport;
- (void) request;
+ // nothing to do with DATA or ACK stage
+ if ( stage != CONTROL_STAGE_SETUP ) return true;
- // nothing to do
- return true;
-}
+ TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
-bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request)
-{
- // Handle class request only
+ // dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
+ if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+ TUSB_REQ_SET_INTERFACE == request->bRequest )
+ {
+ tud_control_status(rhport, request);
+ return true;
+ }
+
+ // Handle class request only from here
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
- switch ( request->bRequest )
+ switch (request->bRequest)
{
case DFU_REQUEST_DETACH:
+ {
+ TU_LOG2(" DFU RT Request: DETACH\r\n");
tud_control_status(rhport, request);
- tud_dfu_rt_reboot_to_dfu();
+ tud_dfu_runtime_reboot_to_dfu_cb();
+ }
break;
- default: return false; // stall unsupported request
- }
+ case DFU_REQUEST_GETSTATUS:
+ {
+ TU_LOG2(" DFU RT Request: GETSTATUS\r\n");
+ dfu_status_response_t resp;
+ // Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
+ memset(&resp, 0x00, sizeof(dfu_status_response_t));
+ tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
+ }
+ break;
- return true;
-}
+ default:
+ {
+ TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
+ return false; // stall unsupported request
+ }
+ }
-bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) rhport;
- (void) ep_addr;
- (void) result;
- (void) xferred_bytes;
return true;
}
diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h
index 4348a0f3a..babaa8214 100644
--- a/src/class/dfu/dfu_rt_device.h
+++ b/src/class/dfu/dfu_rt_device.h
@@ -27,48 +27,25 @@
#ifndef _TUSB_DFU_RT_DEVICE_H_
#define _TUSB_DFU_RT_DEVICE_H_
-#include "common/tusb_common.h"
-#include "device/usbd.h"
+#include "dfu.h"
#ifdef __cplusplus
extern "C" {
#endif
-
-//--------------------------------------------------------------------+
-// Common Definitions
-//--------------------------------------------------------------------+
-
-// DFU Protocol
-typedef enum
-{
- DFU_PROTOCOL_RT = 1,
- DFU_PROTOCOL_DFU = 2,
-} dfu_protocol_type_t;
-
-// DFU Descriptor Type
-typedef enum
-{
- DFU_DESC_FUNCTIONAL = 0x21,
-} dfu_descriptor_type_t;
-
-
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
-
-// Invoked when received new data
-TU_ATTR_WEAK void tud_dfu_rt_reboot_to_dfu(void);
+// Invoked when a DFU_DETACH request is received and bitWillDetach is set
+void tud_dfu_runtime_reboot_to_dfu_cb(void);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void dfu_rtd_init(void);
-void dfu_rtd_reset(uint8_t rhport);
-bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request);
-bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request);
-bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void dfu_rtd_init(void);
+void dfu_rtd_reset(uint8_t rhport);
+uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
#ifdef __cplusplus
}
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index 848274037..9265a2ede 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -62,15 +62,15 @@ typedef enum
{
HID_SUBCLASS_NONE = 0, ///< No Subclass
HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass
-}hid_subclass_type_t;
+}hid_subclass_enum_t;
-/// HID Protocol
+/// HID Interface Protocol
typedef enum
{
- HID_PROTOCOL_NONE = 0, ///< None
- HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard
- HID_PROTOCOL_MOUSE = 2 ///< Mouse
-}hid_protocol_type_t;
+ HID_ITF_PROTOCOL_NONE = 0, ///< None
+ HID_ITF_PROTOCOL_KEYBOARD = 1, ///< Keyboard
+ HID_ITF_PROTOCOL_MOUSE = 2 ///< Mouse
+}hid_interface_protocol_enum_t;
/// HID Descriptor Type
typedef enum
@@ -78,7 +78,7 @@ typedef enum
HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor
HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor
HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor
-}hid_descriptor_type_t;
+}hid_descriptor_enum_t;
/// HID Request Report Type
typedef enum
@@ -98,9 +98,9 @@ typedef enum
HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report
HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle
HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol
-}hid_request_type_t;
+}hid_request_enum_t;
-/// HID Country Code
+/// HID Local Code
typedef enum
{
HID_LOCAL_NotSupported = 0 , ///< NotSupported
@@ -139,11 +139,152 @@ typedef enum
HID_LOCAL_US , ///< US
HID_LOCAL_Yugoslavia , ///< Yugoslavia
HID_LOCAL_Turkish_F ///< Turkish-F
-} hid_country_code_t;
+} hid_local_enum_t;
+
+// HID protocol value used by GetProtocol / SetProtocol
+typedef enum
+{
+ HID_PROTOCOL_BOOT = 0,
+ HID_PROTOCOL_REPORT = 1
+} hid_protocol_mode_enum_t;
/** @} */
//--------------------------------------------------------------------+
+// GAMEPAD
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Gamepad Gamepad
+ * @{ */
+
+/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
+ ____________________________ __
+ / [__ZL__] [__ZR__] \ |
+ / [__ TL __] [__ TR __] \ | Front Triggers
+ __/________________________________\__ __|
+ / _ \ |
+ / /\ __ (N) \ |
+ / || __ |MO| __ _ _ \ | Main Pad
+ | <===DP===> |SE| |ST| (W) -|- (E) | |
+ \ || ___ ___ _ / |
+ /\ \/ / \ / \ (S) /\ __|
+ / \________ | LS | ____ | RS | ________/ \ |
+| / \ \___/ / \ \___/ / \ | | Control Sticks
+| / \_____/ \_____/ \ | __|
+| / \ |
+ \_____/ \_____/
+
+ |________|______| |______|___________|
+ D-Pad Left Right Action Pad
+ Stick Stick
+
+ |_____________|
+ Menu Pad
+
+ Most gamepads have the following features:
+ - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST.
+ - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right.
+ - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START.
+ - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also
+ provide a digital button if you press them.
+ - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons
+ are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
+ - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors.
+ */
+
+/// HID Gamepad Protocol Report.
+typedef struct TU_ATTR_PACKED
+{
+ int8_t x; ///< Delta x movement of left analog-stick
+ int8_t y; ///< Delta y movement of left analog-stick
+ int8_t z; ///< Delta z movement of right analog-joystick
+ int8_t rz; ///< Delta Rz movement of right analog-joystick
+ int8_t rx; ///< Delta Rx movement of analog left trigger
+ int8_t ry; ///< Delta Ry movement of analog right trigger
+ uint8_t hat; ///< Buttons mask for currently pressed buttons in the DPad/hat
+ uint32_t buttons; ///< Buttons mask for currently pressed buttons
+}hid_gamepad_report_t;
+
+/// Standard Gamepad Buttons Bitmap
+typedef enum
+{
+ GAMEPAD_BUTTON_0 = TU_BIT(0),
+ GAMEPAD_BUTTON_1 = TU_BIT(1),
+ GAMEPAD_BUTTON_2 = TU_BIT(2),
+ GAMEPAD_BUTTON_3 = TU_BIT(3),
+ GAMEPAD_BUTTON_4 = TU_BIT(4),
+ GAMEPAD_BUTTON_5 = TU_BIT(5),
+ GAMEPAD_BUTTON_6 = TU_BIT(6),
+ GAMEPAD_BUTTON_7 = TU_BIT(7),
+ GAMEPAD_BUTTON_8 = TU_BIT(8),
+ GAMEPAD_BUTTON_9 = TU_BIT(9),
+ GAMEPAD_BUTTON_10 = TU_BIT(10),
+ GAMEPAD_BUTTON_11 = TU_BIT(11),
+ GAMEPAD_BUTTON_12 = TU_BIT(12),
+ GAMEPAD_BUTTON_13 = TU_BIT(13),
+ GAMEPAD_BUTTON_14 = TU_BIT(14),
+ GAMEPAD_BUTTON_15 = TU_BIT(15),
+ GAMEPAD_BUTTON_16 = TU_BIT(16),
+ GAMEPAD_BUTTON_17 = TU_BIT(17),
+ GAMEPAD_BUTTON_18 = TU_BIT(18),
+ GAMEPAD_BUTTON_19 = TU_BIT(19),
+ GAMEPAD_BUTTON_20 = TU_BIT(20),
+ GAMEPAD_BUTTON_21 = TU_BIT(21),
+ GAMEPAD_BUTTON_22 = TU_BIT(22),
+ GAMEPAD_BUTTON_23 = TU_BIT(23),
+ GAMEPAD_BUTTON_24 = TU_BIT(24),
+ GAMEPAD_BUTTON_25 = TU_BIT(25),
+ GAMEPAD_BUTTON_26 = TU_BIT(26),
+ GAMEPAD_BUTTON_27 = TU_BIT(27),
+ GAMEPAD_BUTTON_28 = TU_BIT(28),
+ GAMEPAD_BUTTON_29 = TU_BIT(29),
+ GAMEPAD_BUTTON_30 = TU_BIT(30),
+ GAMEPAD_BUTTON_31 = TU_BIT(31),
+}hid_gamepad_button_bm_t;
+
+/// Standard Gamepad Buttons Naming from Linux input event codes
+/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
+#define GAMEPAD_BUTTON_A GAMEPAD_BUTTON_0
+#define GAMEPAD_BUTTON_SOUTH GAMEPAD_BUTTON_0
+
+#define GAMEPAD_BUTTON_B GAMEPAD_BUTTON_1
+#define GAMEPAD_BUTTON_EAST GAMEPAD_BUTTON_1
+
+#define GAMEPAD_BUTTON_C GAMEPAD_BUTTON_2
+
+#define GAMEPAD_BUTTON_X GAMEPAD_BUTTON_3
+#define GAMEPAD_BUTTON_NORTH GAMEPAD_BUTTON_3
+
+#define GAMEPAD_BUTTON_Y GAMEPAD_BUTTON_4
+#define GAMEPAD_BUTTON_WEST GAMEPAD_BUTTON_4
+
+#define GAMEPAD_BUTTON_Z GAMEPAD_BUTTON_5
+#define GAMEPAD_BUTTON_TL GAMEPAD_BUTTON_6
+#define GAMEPAD_BUTTON_TR GAMEPAD_BUTTON_7
+#define GAMEPAD_BUTTON_TL2 GAMEPAD_BUTTON_8
+#define GAMEPAD_BUTTON_TR2 GAMEPAD_BUTTON_9
+#define GAMEPAD_BUTTON_SELECT GAMEPAD_BUTTON_10
+#define GAMEPAD_BUTTON_START GAMEPAD_BUTTON_11
+#define GAMEPAD_BUTTON_MODE GAMEPAD_BUTTON_12
+#define GAMEPAD_BUTTON_THUMBL GAMEPAD_BUTTON_13
+#define GAMEPAD_BUTTON_THUMBR GAMEPAD_BUTTON_14
+
+/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes)
+typedef enum
+{
+ GAMEPAD_HAT_CENTERED = 0, ///< DPAD_CENTERED
+ GAMEPAD_HAT_UP = 1, ///< DPAD_UP
+ GAMEPAD_HAT_UP_RIGHT = 2, ///< DPAD_UP_RIGHT
+ GAMEPAD_HAT_RIGHT = 3, ///< DPAD_RIGHT
+ GAMEPAD_HAT_DOWN_RIGHT = 4, ///< DPAD_DOWN_RIGHT
+ GAMEPAD_HAT_DOWN = 5, ///< DPAD_DOWN
+ GAMEPAD_HAT_DOWN_LEFT = 6, ///< DPAD_DOWN_LEFT
+ GAMEPAD_HAT_LEFT = 7, ///< DPAD_LEFT
+ GAMEPAD_HAT_UP_LEFT = 8, ///< DPAD_UP_LEFT
+}hid_gamepad_hat_t;
+
+/// @}
+
+//--------------------------------------------------------------------+
// MOUSE
//--------------------------------------------------------------------+
/** \addtogroup ClassDriver_HID_Mouse Mouse
@@ -212,123 +353,183 @@ typedef enum
//--------------------------------------------------------------------+
// HID KEYCODE
//--------------------------------------------------------------------+
-#define HID_KEY_NONE 0x00
-#define HID_KEY_A 0x04
-#define HID_KEY_B 0x05
-#define HID_KEY_C 0x06
-#define HID_KEY_D 0x07
-#define HID_KEY_E 0x08
-#define HID_KEY_F 0x09
-#define HID_KEY_G 0x0A
-#define HID_KEY_H 0x0B
-#define HID_KEY_I 0x0C
-#define HID_KEY_J 0x0D
-#define HID_KEY_K 0x0E
-#define HID_KEY_L 0x0F
-#define HID_KEY_M 0x10
-#define HID_KEY_N 0x11
-#define HID_KEY_O 0x12
-#define HID_KEY_P 0x13
-#define HID_KEY_Q 0x14
-#define HID_KEY_R 0x15
-#define HID_KEY_S 0x16
-#define HID_KEY_T 0x17
-#define HID_KEY_U 0x18
-#define HID_KEY_V 0x19
-#define HID_KEY_W 0x1A
-#define HID_KEY_X 0x1B
-#define HID_KEY_Y 0x1C
-#define HID_KEY_Z 0x1D
-#define HID_KEY_1 0x1E
-#define HID_KEY_2 0x1F
-#define HID_KEY_3 0x20
-#define HID_KEY_4 0x21
-#define HID_KEY_5 0x22
-#define HID_KEY_6 0x23
-#define HID_KEY_7 0x24
-#define HID_KEY_8 0x25
-#define HID_KEY_9 0x26
-#define HID_KEY_0 0x27
-#define HID_KEY_RETURN 0x28
-#define HID_KEY_ESCAPE 0x29
-#define HID_KEY_BACKSPACE 0x2A
-#define HID_KEY_TAB 0x2B
-#define HID_KEY_SPACE 0x2C
-#define HID_KEY_MINUS 0x2D
-#define HID_KEY_EQUAL 0x2E
-#define HID_KEY_BRACKET_LEFT 0x2F
-#define HID_KEY_BRACKET_RIGHT 0x30
-#define HID_KEY_BACKSLASH 0x31
-#define HID_KEY_EUROPE_1 0x32
-#define HID_KEY_SEMICOLON 0x33
-#define HID_KEY_APOSTROPHE 0x34
-#define HID_KEY_GRAVE 0x35
-#define HID_KEY_COMMA 0x36
-#define HID_KEY_PERIOD 0x37
-#define HID_KEY_SLASH 0x38
-#define HID_KEY_CAPS_LOCK 0x39
-#define HID_KEY_F1 0x3A
-#define HID_KEY_F2 0x3B
-#define HID_KEY_F3 0x3C
-#define HID_KEY_F4 0x3D
-#define HID_KEY_F5 0x3E
-#define HID_KEY_F6 0x3F
-#define HID_KEY_F7 0x40
-#define HID_KEY_F8 0x41
-#define HID_KEY_F9 0x42
-#define HID_KEY_F10 0x43
-#define HID_KEY_F11 0x44
-#define HID_KEY_F12 0x45
-#define HID_KEY_PRINT_SCREEN 0x46
-#define HID_KEY_SCROLL_LOCK 0x47
-#define HID_KEY_PAUSE 0x48
-#define HID_KEY_INSERT 0x49
-#define HID_KEY_HOME 0x4A
-#define HID_KEY_PAGE_UP 0x4B
-#define HID_KEY_DELETE 0x4C
-#define HID_KEY_END 0x4D
-#define HID_KEY_PAGE_DOWN 0x4E
-#define HID_KEY_ARROW_RIGHT 0x4F
-#define HID_KEY_ARROW_LEFT 0x50
-#define HID_KEY_ARROW_DOWN 0x51
-#define HID_KEY_ARROW_UP 0x52
-#define HID_KEY_NUM_LOCK 0x53
-#define HID_KEY_KEYPAD_DIVIDE 0x54
-#define HID_KEY_KEYPAD_MULTIPLY 0x55
-#define HID_KEY_KEYPAD_SUBTRACT 0x56
-#define HID_KEY_KEYPAD_ADD 0x57
-#define HID_KEY_KEYPAD_ENTER 0x58
-#define HID_KEY_KEYPAD_1 0x59
-#define HID_KEY_KEYPAD_2 0x5A
-#define HID_KEY_KEYPAD_3 0x5B
-#define HID_KEY_KEYPAD_4 0x5C
-#define HID_KEY_KEYPAD_5 0x5D
-#define HID_KEY_KEYPAD_6 0x5E
-#define HID_KEY_KEYPAD_7 0x5F
-#define HID_KEY_KEYPAD_8 0x60
-#define HID_KEY_KEYPAD_9 0x61
-#define HID_KEY_KEYPAD_0 0x62
-#define HID_KEY_KEYPAD_DECIMAL 0x63
-#define HID_KEY_EUROPE_2 0x64
-#define HID_KEY_APPLICATION 0x65
-#define HID_KEY_POWER 0x66
-#define HID_KEY_KEYPAD_EQUAL 0x67
-#define HID_KEY_F13 0x68
-#define HID_KEY_F14 0x69
-#define HID_KEY_F15 0x6A
-#define HID_KEY_CONTROL_LEFT 0xE0
-#define HID_KEY_SHIFT_LEFT 0xE1
-#define HID_KEY_ALT_LEFT 0xE2
-#define HID_KEY_GUI_LEFT 0xE3
-#define HID_KEY_CONTROL_RIGHT 0xE4
-#define HID_KEY_SHIFT_RIGHT 0xE5
-#define HID_KEY_ALT_RIGHT 0xE6
-#define HID_KEY_GUI_RIGHT 0xE7
+#define HID_KEY_NONE 0x00
+#define HID_KEY_A 0x04
+#define HID_KEY_B 0x05
+#define HID_KEY_C 0x06
+#define HID_KEY_D 0x07
+#define HID_KEY_E 0x08
+#define HID_KEY_F 0x09
+#define HID_KEY_G 0x0A
+#define HID_KEY_H 0x0B
+#define HID_KEY_I 0x0C
+#define HID_KEY_J 0x0D
+#define HID_KEY_K 0x0E
+#define HID_KEY_L 0x0F
+#define HID_KEY_M 0x10
+#define HID_KEY_N 0x11
+#define HID_KEY_O 0x12
+#define HID_KEY_P 0x13
+#define HID_KEY_Q 0x14
+#define HID_KEY_R 0x15
+#define HID_KEY_S 0x16
+#define HID_KEY_T 0x17
+#define HID_KEY_U 0x18
+#define HID_KEY_V 0x19
+#define HID_KEY_W 0x1A
+#define HID_KEY_X 0x1B
+#define HID_KEY_Y 0x1C
+#define HID_KEY_Z 0x1D
+#define HID_KEY_1 0x1E
+#define HID_KEY_2 0x1F
+#define HID_KEY_3 0x20
+#define HID_KEY_4 0x21
+#define HID_KEY_5 0x22
+#define HID_KEY_6 0x23
+#define HID_KEY_7 0x24
+#define HID_KEY_8 0x25
+#define HID_KEY_9 0x26
+#define HID_KEY_0 0x27
+#define HID_KEY_ENTER 0x28
+#define HID_KEY_ESCAPE 0x29
+#define HID_KEY_BACKSPACE 0x2A
+#define HID_KEY_TAB 0x2B
+#define HID_KEY_SPACE 0x2C
+#define HID_KEY_MINUS 0x2D
+#define HID_KEY_EQUAL 0x2E
+#define HID_KEY_BRACKET_LEFT 0x2F
+#define HID_KEY_BRACKET_RIGHT 0x30
+#define HID_KEY_BACKSLASH 0x31
+#define HID_KEY_EUROPE_1 0x32
+#define HID_KEY_SEMICOLON 0x33
+#define HID_KEY_APOSTROPHE 0x34
+#define HID_KEY_GRAVE 0x35
+#define HID_KEY_COMMA 0x36
+#define HID_KEY_PERIOD 0x37
+#define HID_KEY_SLASH 0x38
+#define HID_KEY_CAPS_LOCK 0x39
+#define HID_KEY_F1 0x3A
+#define HID_KEY_F2 0x3B
+#define HID_KEY_F3 0x3C
+#define HID_KEY_F4 0x3D
+#define HID_KEY_F5 0x3E
+#define HID_KEY_F6 0x3F
+#define HID_KEY_F7 0x40
+#define HID_KEY_F8 0x41
+#define HID_KEY_F9 0x42
+#define HID_KEY_F10 0x43
+#define HID_KEY_F11 0x44
+#define HID_KEY_F12 0x45
+#define HID_KEY_PRINT_SCREEN 0x46
+#define HID_KEY_SCROLL_LOCK 0x47
+#define HID_KEY_PAUSE 0x48
+#define HID_KEY_INSERT 0x49
+#define HID_KEY_HOME 0x4A
+#define HID_KEY_PAGE_UP 0x4B
+#define HID_KEY_DELETE 0x4C
+#define HID_KEY_END 0x4D
+#define HID_KEY_PAGE_DOWN 0x4E
+#define HID_KEY_ARROW_RIGHT 0x4F
+#define HID_KEY_ARROW_LEFT 0x50
+#define HID_KEY_ARROW_DOWN 0x51
+#define HID_KEY_ARROW_UP 0x52
+#define HID_KEY_NUM_LOCK 0x53
+#define HID_KEY_KEYPAD_DIVIDE 0x54
+#define HID_KEY_KEYPAD_MULTIPLY 0x55
+#define HID_KEY_KEYPAD_SUBTRACT 0x56
+#define HID_KEY_KEYPAD_ADD 0x57
+#define HID_KEY_KEYPAD_ENTER 0x58
+#define HID_KEY_KEYPAD_1 0x59
+#define HID_KEY_KEYPAD_2 0x5A
+#define HID_KEY_KEYPAD_3 0x5B
+#define HID_KEY_KEYPAD_4 0x5C
+#define HID_KEY_KEYPAD_5 0x5D
+#define HID_KEY_KEYPAD_6 0x5E
+#define HID_KEY_KEYPAD_7 0x5F
+#define HID_KEY_KEYPAD_8 0x60
+#define HID_KEY_KEYPAD_9 0x61
+#define HID_KEY_KEYPAD_0 0x62
+#define HID_KEY_KEYPAD_DECIMAL 0x63
+#define HID_KEY_EUROPE_2 0x64
+#define HID_KEY_APPLICATION 0x65
+#define HID_KEY_POWER 0x66
+#define HID_KEY_KEYPAD_EQUAL 0x67
+#define HID_KEY_F13 0x68
+#define HID_KEY_F14 0x69
+#define HID_KEY_F15 0x6A
+#define HID_KEY_F16 0x6B
+#define HID_KEY_F17 0x6C
+#define HID_KEY_F18 0x6D
+#define HID_KEY_F19 0x6E
+#define HID_KEY_F20 0x6F
+#define HID_KEY_F21 0x70
+#define HID_KEY_F22 0x71
+#define HID_KEY_F23 0x72
+#define HID_KEY_F24 0x73
+#define HID_KEY_EXECUTE 0x74
+#define HID_KEY_HELP 0x75
+#define HID_KEY_MENU 0x76
+#define HID_KEY_SELECT 0x77
+#define HID_KEY_STOP 0x78
+#define HID_KEY_AGAIN 0x79
+#define HID_KEY_UNDO 0x7A
+#define HID_KEY_CUT 0x7B
+#define HID_KEY_COPY 0x7C
+#define HID_KEY_PASTE 0x7D
+#define HID_KEY_FIND 0x7E
+#define HID_KEY_MUTE 0x7F
+#define HID_KEY_VOLUME_UP 0x80
+#define HID_KEY_VOLUME_DOWN 0x81
+#define HID_KEY_LOCKING_CAPS_LOCK 0x82
+#define HID_KEY_LOCKING_NUM_LOCK 0x83
+#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
+#define HID_KEY_KEYPAD_COMMA 0x85
+#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
+#define HID_KEY_KANJI1 0x87
+#define HID_KEY_KANJI2 0x88
+#define HID_KEY_KANJI3 0x89
+#define HID_KEY_KANJI4 0x8A
+#define HID_KEY_KANJI5 0x8B
+#define HID_KEY_KANJI6 0x8C
+#define HID_KEY_KANJI7 0x8D
+#define HID_KEY_KANJI8 0x8E
+#define HID_KEY_KANJI9 0x8F
+#define HID_KEY_LANG1 0x90
+#define HID_KEY_LANG2 0x91
+#define HID_KEY_LANG3 0x92
+#define HID_KEY_LANG4 0x93
+#define HID_KEY_LANG5 0x94
+#define HID_KEY_LANG6 0x95
+#define HID_KEY_LANG7 0x96
+#define HID_KEY_LANG8 0x97
+#define HID_KEY_LANG9 0x98
+#define HID_KEY_ALTERNATE_ERASE 0x99
+#define HID_KEY_SYSREQ_ATTENTION 0x9A
+#define HID_KEY_CANCEL 0x9B
+#define HID_KEY_CLEAR 0x9C
+#define HID_KEY_PRIOR 0x9D
+#define HID_KEY_RETURN 0x9E
+#define HID_KEY_SEPARATOR 0x9F
+#define HID_KEY_OUT 0xA0
+#define HID_KEY_OPER 0xA1
+#define HID_KEY_CLEAR_AGAIN 0xA2
+#define HID_KEY_CRSEL_PROPS 0xA3
+#define HID_KEY_EXSEL 0xA4
+// RESERVED 0xA5-DF
+#define HID_KEY_CONTROL_LEFT 0xE0
+#define HID_KEY_SHIFT_LEFT 0xE1
+#define HID_KEY_ALT_LEFT 0xE2
+#define HID_KEY_GUI_LEFT 0xE3
+#define HID_KEY_CONTROL_RIGHT 0xE4
+#define HID_KEY_SHIFT_RIGHT 0xE5
+#define HID_KEY_ALT_RIGHT 0xE6
+#define HID_KEY_GUI_RIGHT 0xE7
//--------------------------------------------------------------------+
// REPORT DESCRIPTOR
//--------------------------------------------------------------------+
+
//------------- ITEM & TAG -------------//
#define HID_REPORT_DATA_0(data)
#define HID_REPORT_DATA_1(data) , data
@@ -338,18 +539,31 @@ typedef enum
#define HID_REPORT_ITEM(data, tag, type, size) \
(((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data)
-#define RI_TYPE_MAIN 0
-#define RI_TYPE_GLOBAL 1
-#define RI_TYPE_LOCAL 2
+// Report Item Types
+enum {
+ RI_TYPE_MAIN = 0,
+ RI_TYPE_GLOBAL = 1,
+ RI_TYPE_LOCAL = 2
+};
+
+//------------- Main Items - HID 1.11 section 6.2.2.4 -------------//
-//------------- MAIN ITEMS 6.2.2.4 -------------//
-#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1)
-#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1)
-#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1)
-#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1)
-#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0)
+// Report Item Main group
+enum {
+ RI_MAIN_INPUT = 8,
+ RI_MAIN_OUTPUT = 9,
+ RI_MAIN_COLLECTION = 10,
+ RI_MAIN_FEATURE = 11,
+ RI_MAIN_COLLECTION_END = 12
+};
+
+#define HID_INPUT(x) HID_REPORT_ITEM(x, RI_MAIN_INPUT , RI_TYPE_MAIN, 1)
+#define HID_OUTPUT(x) HID_REPORT_ITEM(x, RI_MAIN_OUTPUT , RI_TYPE_MAIN, 1)
+#define HID_COLLECTION(x) HID_REPORT_ITEM(x, RI_MAIN_COLLECTION , RI_TYPE_MAIN, 1)
+#define HID_FEATURE(x) HID_REPORT_ITEM(x, RI_MAIN_FEATURE , RI_TYPE_MAIN, 1)
+#define HID_COLLECTION_END HID_REPORT_ITEM(x, RI_MAIN_COLLECTION_END, RI_TYPE_MAIN, 0)
-//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------//
+//------------- Input, Output, Feature - HID 1.11 section 6.2.2.5 -------------//
#define HID_DATA (0<<0)
#define HID_CONSTANT (1<<0)
@@ -377,7 +591,7 @@ typedef enum
#define HID_BITFIELD (0<<8)
#define HID_BUFFERED_BYTES (1<<8)
-//------------- COLLECTION ITEM 6.2.2.6 -------------//
+//------------- Collection Item - HID 1.11 section 6.2.2.6 -------------//
enum {
HID_COLLECTION_PHYSICAL = 0,
HID_COLLECTION_APPLICATION,
@@ -388,49 +602,81 @@ enum {
HID_COLLECTION_USAGE_MODIFIER
};
-//------------- GLOBAL ITEMS 6.2.2.7 -------------//
-#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1)
-#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n)
+//------------- Global Items - HID 1.11 section 6.2.2.7 -------------//
+
+// Report Item Global group
+enum {
+ RI_GLOBAL_USAGE_PAGE = 0,
+ RI_GLOBAL_LOGICAL_MIN = 1,
+ RI_GLOBAL_LOGICAL_MAX = 2,
+ RI_GLOBAL_PHYSICAL_MIN = 3,
+ RI_GLOBAL_PHYSICAL_MAX = 4,
+ RI_GLOBAL_UNIT_EXPONENT = 5,
+ RI_GLOBAL_UNIT = 6,
+ RI_GLOBAL_REPORT_SIZE = 7,
+ RI_GLOBAL_REPORT_ID = 8,
+ RI_GLOBAL_REPORT_COUNT = 9,
+ RI_GLOBAL_PUSH = 10,
+ RI_GLOBAL_POP = 11
+};
-#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1)
-#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n)
+#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, RI_GLOBAL_USAGE_PAGE, RI_TYPE_GLOBAL, 1)
+#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_USAGE_PAGE, RI_TYPE_GLOBAL, n)
-#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1)
-#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n)
+#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MIN, RI_TYPE_GLOBAL, 1)
+#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MIN, RI_TYPE_GLOBAL, n)
-#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1)
-#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n)
+#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MAX, RI_TYPE_GLOBAL, 1)
+#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_LOGICAL_MAX, RI_TYPE_GLOBAL, n)
-#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1)
-#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n)
+#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MIN, RI_TYPE_GLOBAL, 1)
+#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MIN, RI_TYPE_GLOBAL, n)
-#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1)
-#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n)
+#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MAX, RI_TYPE_GLOBAL, 1)
+#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_PHYSICAL_MAX, RI_TYPE_GLOBAL, n)
-#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1)
-#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n)
+#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT_EXPONENT, RI_TYPE_GLOBAL, 1)
+#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT_EXPONENT, RI_TYPE_GLOBAL, n)
-#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n)
+#define HID_UNIT(x) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT, RI_TYPE_GLOBAL, 1)
+#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_UNIT, RI_TYPE_GLOBAL, n)
-#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n)
+#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_SIZE, RI_TYPE_GLOBAL, 1)
+#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_SIZE, RI_TYPE_GLOBAL, n)
-#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n)
+#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, 1),
+#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_ID, RI_TYPE_GLOBAL, n),
-#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0)
-#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0)
+#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, 1)
+#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, RI_GLOBAL_REPORT_COUNT, RI_TYPE_GLOBAL, n)
+
+#define HID_PUSH HID_REPORT_ITEM(x, RI_GLOBAL_PUSH, RI_TYPE_GLOBAL, 0)
+#define HID_POP HID_REPORT_ITEM(x, RI_GLOBAL_POP, RI_TYPE_GLOBAL, 0)
//------------- LOCAL ITEMS 6.2.2.8 -------------//
-#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n)
-#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n)
+enum {
+ RI_LOCAL_USAGE = 0,
+ RI_LOCAL_USAGE_MIN = 1,
+ RI_LOCAL_USAGE_MAX = 2,
+ RI_LOCAL_DESIGNATOR_INDEX = 3,
+ RI_LOCAL_DESIGNATOR_MIN = 4,
+ RI_LOCAL_DESIGNATOR_MAX = 5,
+ // 6 is reserved
+ RI_LOCAL_STRING_INDEX = 7,
+ RI_LOCAL_STRING_MIN = 8,
+ RI_LOCAL_STRING_MAX = 9,
+ RI_LOCAL_DELIMITER = 10,
+};
+
+#define HID_USAGE(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE, RI_TYPE_LOCAL, n)
+
+#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MIN, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MIN, RI_TYPE_LOCAL, n)
-#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n)
+#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MAX, RI_TYPE_LOCAL, 1)
+#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, RI_LOCAL_USAGE_MAX, RI_TYPE_LOCAL, n)
//--------------------------------------------------------------------+
// Usage Table
@@ -625,10 +871,10 @@ enum
{0, 0 }, /* 0x07 */ \
{0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
{0, HID_KEY_TAB }, /* 0x09 Tab */ \
- {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
+ {0, HID_KEY_ENTER }, /* 0x0A Line Feed */ \
{0, 0 }, /* 0x0B */ \
{0, 0 }, /* 0x0C */ \
- {0, HID_KEY_RETURN }, /* 0x0D CR */ \
+ {0, HID_KEY_ENTER }, /* 0x0D CR */ \
{0, 0 }, /* 0x0E */ \
{0, 0 }, /* 0x0F */ \
{0, 0 }, /* 0x10 */ \
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index 7cb35f1ce..2ee80750a 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -31,10 +31,11 @@
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
-#include "hid_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "hid_device.h"
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -43,50 +44,54 @@ typedef struct
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out; // optional Out endpoint
- uint8_t boot_protocol; // Boot mouse or keyboard
- bool boot_mode; // default = false (Report)
+ uint8_t itf_protocol; // Boot mouse or keyboard
+
+ uint8_t protocol_mode; // Boot (0) or Report protocol (1)
uint8_t idle_rate; // up to application to handle idle rate
uint16_t report_desc_len;
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
+ // TODO save hid descriptor since host can specifically request this after enumeration
+ // Note: HID descriptor may be not available from application after enumeration
tusb_hid_descriptor_hid_t const * hid_descriptor;
} hidd_interface_t;
CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
/*------------- Helpers -------------*/
-static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
+static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
{
- for (uint8_t i=0; i < CFG_TUD_HID; i++ )
- {
- if ( itf_num == _hidd_itf[i].itf_num ) return &_hidd_itf[i];
- }
+ for (uint8_t i=0; i < CFG_TUD_HID; i++ )
+ {
+ if ( itf_num == _hidd_itf[i].itf_num ) return i;
+ }
- return NULL;
+ return 0xFF;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_hid_ready(void)
+bool tud_hid_n_ready(uint8_t instance)
{
- uint8_t itf = 0;
- uint8_t const ep_in = _hidd_itf[itf].ep_in;
- return tud_ready() && (ep_in != 0) && usbd_edpt_ready(TUD_OPT_RHPORT, ep_in);
+ uint8_t const ep_in = _hidd_itf[instance].ep_in;
+ return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in);
}
-bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
+bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len)
{
- TU_VERIFY( tud_hid_ready() );
+ uint8_t const rhport = 0;
+ hidd_interface_t * p_hid = &_hidd_itf[instance];
- uint8_t itf = 0;
- hidd_interface_t * p_hid = &_hidd_itf[itf];
+ // claim endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+ // prepare data
if (report_id)
{
- len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1);
+ len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
p_hid->epin_buf[0] = report_id;
memcpy(p_hid->epin_buf+1, report, len);
@@ -94,27 +99,29 @@ bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
}else
{
// If report id = 0, skip ID field
- len = tu_min8(len, CFG_TUD_HID_BUFSIZE);
+ len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE);
memcpy(p_hid->epin_buf, report, len);
}
return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
}
-bool tud_hid_boot_mode(void)
+uint8_t tud_hid_n_interface_protocol(uint8_t instance)
{
- uint8_t itf = 0;
- return _hidd_itf[itf].boot_mode;
+ return _hidd_itf[instance].itf_protocol;
}
-//--------------------------------------------------------------------+
-// KEYBOARD API
-//--------------------------------------------------------------------+
-bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+uint8_t tud_hid_n_get_protocol(uint8_t instance)
+{
+ return _hidd_itf[instance].protocol_mode;
+}
+
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
{
hid_keyboard_report_t report;
report.modifier = modifier;
+ report.reserved = 0;
if ( keycode )
{
@@ -124,13 +131,11 @@ bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycod
tu_memclr(report.keycode, 6);
}
- return tud_hid_report(report_id, &report, sizeof(report));
+ return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
-//--------------------------------------------------------------------+
-// MOUSE APPLICATION API
-//--------------------------------------------------------------------+
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
+ uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
hid_mouse_report_t report =
{
@@ -141,7 +146,25 @@ bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y
.pan = horizontal
};
- return tud_hid_report(report_id, &report, sizeof(report));
+ return tud_hid_n_report(instance, report_id, &report, sizeof(report));
+}
+
+bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
+ int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
+{
+ hid_gamepad_report_t report =
+ {
+ .x = x,
+ .y = y,
+ .z = z,
+ .rz = rz,
+ .rx = rx,
+ .ry = ry,
+ .hat = hat,
+ .buttons = buttons,
+ };
+
+ return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
@@ -158,9 +181,13 @@ void hidd_reset(uint8_t rhport)
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
-bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
+uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
- uint8_t const *p_desc = (uint8_t const *) desc_itf;
+ TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
+
+ // len = interface + hid + n*endpoints
+ uint16_t const drv_len = 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, 0);
// Find available interface
hidd_interface_t * p_hid = NULL;
@@ -173,60 +200,74 @@ bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t
break;
}
}
- TU_ASSERT(p_hid);
+ TU_ASSERT(p_hid, 0);
+
+ uint8_t const *p_desc = (uint8_t const *) desc_itf;
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
- TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType);
+ TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType, 0);
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in));
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
- if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
+ if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
- p_hid->boot_mode = false; // default mode is REPORT
+ p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
p_hid->itf_num = desc_itf->bInterfaceNumber;
- memcpy(&p_hid->report_desc_len, &(p_hid->hid_descriptor->wReportLength), 2);
- *p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ // Use offsetof to avoid pointer to the odd/misaligned address
+ memcpy(&p_hid->report_desc_len, (uint8_t*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength), 2);
// Prepare for output endpoint
- if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+ if (p_hid->ep_out)
+ {
+ if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
+ {
+ TU_LOG_FAILED();
+ TU_BREAKPOINT();
+ }
+ }
- return true;
+ return drv_len;
}
-// Handle class control request
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request)
+bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
- hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) request->wIndex );
- TU_ASSERT(p_hid);
+ uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+ TU_VERIFY(hid_itf < CFG_TUD_HID);
+
+ hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
{
//------------- STD Request -------------//
- uint8_t const desc_type = tu_u16_high(request->wValue);
- uint8_t const desc_index = tu_u16_low (request->wValue);
- (void) desc_index;
-
- if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
+ if ( stage == CONTROL_STAGE_SETUP )
{
- TU_VERIFY(p_hid->hid_descriptor != NULL);
- TU_VERIFY(tud_control_xfer(rhport, request, (void*) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
- }
- else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
- {
- uint8_t const * desc_report = tud_hid_descriptor_report_cb();
- tud_control_xfer(rhport, request, (void*) desc_report, p_hid->report_desc_len);
- }
- else
- {
- return false; // stall unsupported request
+ uint8_t const desc_type = tu_u16_high(request->wValue);
+ //uint8_t const desc_index = tu_u16_low (request->wValue);
+
+ if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
+ {
+ TU_VERIFY(p_hid->hid_descriptor != NULL);
+ TU_VERIFY(tud_control_xfer(rhport, request, (void*) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
+ }
+ else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
+ {
+ uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
+ tud_control_xfer(rhport, request, (void*) desc_report, p_hid->report_desc_len);
+ }
+ else
+ {
+ return false; // stall unsupported request
+ }
}
}
else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
@@ -235,52 +276,99 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
switch( request->bRequest )
{
case HID_REQ_CONTROL_GET_REPORT:
- {
- // wValue = Report Type | Report ID
- uint8_t const report_type = tu_u16_high(request->wValue);
- uint8_t const report_id = tu_u16_low(request->wValue);
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ uint8_t const report_type = tu_u16_high(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
- uint16_t xferlen = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength);
- TU_ASSERT( xferlen > 0 );
+ uint8_t* report_buf = p_hid->epin_buf;
+ uint16_t req_len = request->wLength;
- tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
- }
+ uint16_t xferlen = 0;
+
+ // If host request a specific Report ID, add ID to as 1 byte of response
+ if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
+ {
+ *report_buf++ = report_id;
+ req_len--;
+
+ xferlen++;
+ }
+
+ xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
+ TU_ASSERT( xferlen > 0 );
+
+ tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
+ }
break;
case HID_REQ_CONTROL_SET_REPORT:
- TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
- tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
+ tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ uint8_t const report_type = tu_u16_high(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
+
+ uint8_t const* report_buf = p_hid->epout_buf;
+ uint16_t report_len = request->wLength;
+
+ // If host request a specific Report ID, extract report ID in buffer before invoking callback
+ if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
+ {
+ report_buf++;
+ report_len--;
+ }
+
+ tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
+ }
break;
case HID_REQ_CONTROL_SET_IDLE:
- p_hid->idle_rate = tu_u16_high(request->wValue);
- if ( tud_hid_set_idle_cb )
+ if ( stage == CONTROL_STAGE_SETUP )
{
- // stall request if callback return false
- if ( !tud_hid_set_idle_cb(p_hid->idle_rate) ) return false;
- }
+ p_hid->idle_rate = tu_u16_high(request->wValue);
+ if ( tud_hid_set_idle_cb )
+ {
+ // stall request if callback return false
+ TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
+ }
- tud_control_status(rhport, request);
+ tud_control_status(rhport, request);
+ }
break;
case HID_REQ_CONTROL_GET_IDLE:
- // TODO idle rate of report
- tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ // TODO idle rate of report
+ tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
+ }
break;
case HID_REQ_CONTROL_GET_PROTOCOL:
- {
- uint8_t protocol = (uint8_t)(1-p_hid->boot_mode); // 0 is Boot, 1 is Report protocol
- tud_control_xfer(rhport, request, &protocol, 1);
- }
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
+ }
break;
case HID_REQ_CONTROL_SET_PROTOCOL:
- p_hid->boot_mode = 1 - request->wValue; // 0 is Boot, 1 is Report protocol
-
- if (tud_hid_boot_mode_cb) tud_hid_boot_mode_cb(p_hid->boot_mode);
-
- tud_control_status(rhport, request);
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ tud_control_status(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_ACK )
+ {
+ p_hid->protocol_mode = (uint8_t) request->wValue;
+ if (tud_hid_set_protocol_cb)
+ {
+ tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
+ }
+ }
break;
default: return false; // stall unsupported request
@@ -293,44 +381,33 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
return true;
}
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool hidd_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- (void) rhport;
- hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
- TU_ASSERT(p_hid);
-
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
- p_request->bRequest == HID_REQ_CONTROL_SET_REPORT)
- {
- // wValue = Report Type | Report ID
- uint8_t const report_type = tu_u16_high(p_request->wValue);
- uint8_t const report_id = tu_u16_low(p_request->wValue);
-
- tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength);
- }
-
- return true;
-}
-
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
- uint8_t itf = 0;
+ uint8_t instance = 0;
hidd_interface_t * p_hid = _hidd_itf;
- for ( ; ; itf++, p_hid++)
+ // Identify which interface to use
+ for (instance = 0; instance < CFG_TUD_HID; instance++)
{
- if (itf >= TU_ARRAY_SIZE(_hidd_itf)) return false;
-
- if ( ep_addr == p_hid->ep_out ) break;
+ p_hid = &_hidd_itf[instance];
+ if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
}
+ TU_ASSERT(instance < CFG_TUD_HID);
- if (ep_addr == p_hid->ep_out)
+ // Sent report successfully
+ if (ep_addr == p_hid->ep_in)
+ {
+ if (tud_hid_report_complete_cb)
+ {
+ tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint8_t) xferred_bytes);
+ }
+ }
+ // Received report
+ else if (ep_addr == p_hid->ep_out)
{
- tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
+ tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
}
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index f5e29d8a2..e2c950dd1 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -27,8 +27,6 @@
#ifndef _TUSB_HID_DEVICE_H_
#define _TUSB_HID_DEVICE_H_
-#include "common/tusb_common.h"
-#include "device/usbd.h"
#include "hid.h"
#ifdef __cplusplus
@@ -39,30 +37,55 @@
// Class Driver Default Configure & Validation
//--------------------------------------------------------------------+
-#ifndef CFG_TUD_HID_BUFSIZE
-#define CFG_TUD_HID_BUFSIZE 16
+#if !defined(CFG_TUD_HID_EP_BUFSIZE) & defined(CFG_TUD_HID_BUFSIZE)
+ // TODO warn user to use new name later on
+ // #warning CFG_TUD_HID_BUFSIZE is renamed to CFG_TUD_HID_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_HID_EP_BUFSIZE CFG_TUD_HID_BUFSIZE
+#endif
+
+#ifndef CFG_TUD_HID_EP_BUFSIZE
+ #define CFG_TUD_HID_EP_BUFSIZE 64
#endif
//--------------------------------------------------------------------+
-// Application API
+// Application API (Multiple Instances)
+// CFG_TUD_HID > 1
//--------------------------------------------------------------------+
// Check if the interface is ready to use
-bool tud_hid_ready(void);
+bool tud_hid_n_ready(uint8_t instance);
+
+// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
+uint8_t tud_hid_n_interface_protocol(uint8_t instance);
-// Check if current mode is Boot (true) or Report (false)
-bool tud_hid_boot_mode(void);
+// Get current active protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+uint8_t tud_hid_n_get_protocol(uint8_t instance);
// Send report to host
-bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
+bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len);
// KEYBOARD: convenient helper to send keyboard report if application
// use template layout report as defined by hid_keyboard_report_t
-bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
// MOUSE: convenient helper to send mouse report if application
// use template layout report as defined by hid_mouse_report_t
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+
+// Gamepad: convenient helper to send gamepad report if application
+// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
+bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void);
+static inline uint8_t tud_hid_interface_protocol(void);
+static inline uint8_t tud_hid_get_protocol(void);
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
//--------------------------------------------------------------------+
// Callbacks (Weak is optional)
@@ -70,40 +93,85 @@ bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y
// Invoked when received GET HID REPORT DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint8_t const * tud_hid_descriptor_report_cb(void);
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance);
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
+uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
+void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
-// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
-TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
+// Invoked when received SET_PROTOCOL request
+// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
// Invoked when received SET_IDLE request. return false will stall the request
// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
+
+// Invoked when sent REPORT successfully to host
+// Application can use this to send the next report
+// Note: For composite reports, report[0] is report ID
+TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len);
+
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void)
+{
+ return tud_hid_n_ready(0);
+}
+
+static inline uint8_t tud_hid_interface_protocol(void)
+{
+ return tud_hid_n_interface_protocol(0);
+}
+
+static inline uint8_t tud_hid_get_protocol(void)
+{
+ return tud_hid_n_get_protocol(0);
+}
+
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
+{
+ return tud_hid_n_report(0, report_id, report, len);
+}
+
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+{
+ return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+{
+ return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
+{
+ return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
+}
/* --------------------------------------------------------------------+
* HID Report Descriptor Template
*
* Convenient for declaring popular HID device (keyboard, mouse, consumer,
- * gamepad etc...). Templates take "HID_REPORT_ID(n)," as input, leave
+ * gamepad etc...). Templates take "HID_REPORT_ID(n)" as input, leave
* empty if multiple reports is not used
*
* - Only 1 report: no parameter
* uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() };
*
- * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template
+ * - Multiple Reports: "HID_REPORT_ID(ID)" must be passed to template
* uint8_t const report_desc[] =
* {
- * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) ,
- * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2), )
+ * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1) ) ,
+ * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2) )
* };
*--------------------------------------------------------------------*/
@@ -130,9 +198,9 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
/* 6-byte Keycodes */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 0 ) ,\
- HID_USAGE_MAX ( 255 ) ,\
+ HID_USAGE_MAX_N ( 255, 2 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 255 ) ,\
+ HID_LOGICAL_MAX_N( 255, 2 ) ,\
HID_REPORT_COUNT ( 6 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
@@ -218,7 +286,7 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
* 0x00 - do nothing
* 0x01 - Power Off
* 0x02 - Standby
- * 0x04 - Wake Host
+ * 0x03 - Wake Host
*/
#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
@@ -231,8 +299,8 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
HID_LOGICAL_MAX ( 3 ) ,\
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 2 ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
/* 6 bit padding */ \
@@ -242,34 +310,46 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
HID_COLLECTION_END \
// Gamepad Report Descriptor Template
-// with 16 buttons and 2 joysticks with following layout
-// | Button Map (2 bytes) | X | Y | Z | Rz
+// with 16 buttons, 2 joysticks and 1 hat/dpad with following layout
+// | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (2 bytes) |
#define TUD_HID_REPORT_DESC_GAMEPAD(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
- /* 16 bit Button Map */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
- HID_USAGE_MIN ( 1 ) ,\
- HID_USAGE_MAX ( 16 ) ,\
- HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 1 ) ,\
- HID_REPORT_COUNT ( 16 ) ,\
- HID_REPORT_SIZE ( 1 ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
- /* X, Y, Z, Rz (min -127, max 127 ) */ \
+ /* 8 bit X, Y, Z, Rz, Rx, Ry (min -127, max 127 ) */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_LOGICAL_MIN ( 0x81 ) ,\
- HID_LOGICAL_MAX ( 0x7f ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\
- HID_REPORT_COUNT ( 4 ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\
+ HID_LOGICAL_MIN ( 0x81 ) ,\
+ HID_LOGICAL_MAX ( 0x7f ) ,\
+ HID_REPORT_COUNT ( 6 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 8 bit DPad/Hat Button Map */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\
+ HID_LOGICAL_MIN ( 1 ) ,\
+ HID_LOGICAL_MAX ( 8 ) ,\
+ HID_PHYSICAL_MIN ( 0 ) ,\
+ HID_PHYSICAL_MAX_N ( 315, 2 ) ,\
+ HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 16 bit Button Map */ \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
+ HID_USAGE_MIN ( 1 ) ,\
+ HID_USAGE_MAX ( 32 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 1 ) ,\
+ HID_REPORT_COUNT ( 32 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
HID_COLLECTION_END \
// HID Generic Input & Output
@@ -300,16 +380,14 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void hidd_init (void);
-void hidd_reset (uint8_t rhport);
-bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
-bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void hidd_init (void);
+void hidd_reset (uint8_t rhport);
+uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_DEVICE_H_ */
-
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index 69c49b012..4dd198369 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -26,264 +26,591 @@
#include "tusb_option.h"
-#if (TUSB_OPT_HOST_ENABLED && HOST_CLASS_HID)
+#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_HID)
+
+#include "host/usbh.h"
+#include "host/usbh_classdriver.h"
-#include "common/tusb_common.h"
#include "hid_host.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-//--------------------------------------------------------------------+
-// HID Interface common functions
-//--------------------------------------------------------------------+
-static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid)
+typedef struct
{
- p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID);
- p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
- p_hid->interface_number = interface_number;
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
- TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl));
+ uint8_t itf_protocol; // None, Keyboard, Mouse
+ uint8_t protocol_mode; // Boot (0) or Report protocol (1)
- return true;
-}
+ uint8_t report_desc_type;
+ uint16_t report_desc_len;
-static inline void hidh_interface_close(hidh_interface_info_t *p_hid)
-{
- tu_memclr(p_hid, sizeof(hidh_interface_info_t));
-}
+ uint16_t epin_size;
+ uint16_t epout_size;
+
+ uint8_t epin_buf[CFG_TUH_HID_EP_BUFSIZE];
+ uint8_t epout_buf[CFG_TUH_HID_EP_BUFSIZE];
+} hidh_interface_t;
-// called from public API need to validate parameters
-tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_info_t *p_hid)
+typedef struct
{
- //------------- parameters validation -------------//
- // TODO change to use is configured function
- TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY);
- TU_VERIFY (report, TUSB_ERROR_INVALID_PARA);
- TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY);
+ uint8_t inst_count;
+ hidh_interface_t instances[CFG_TUH_HID];
+} hidh_device_t;
+
+static hidh_device_t _hidh_dev[CFG_TUSB_HOST_DEVICE_MAX-1];
- TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ;
+//------------- Internal prototypes -------------//
- return TUSB_ERROR_NONE;
+// Get HID device & interface
+TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr);
+TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance);
+static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf);
+static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr);
+
+TU_ATTR_ALWAYS_INLINE static inline bool hidh_get_report(uint8_t dev_addr, hidh_interface_t* hid_itf)
+{
+ return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size);
}
//--------------------------------------------------------------------+
-// KEYBOARD
+// Application API
//--------------------------------------------------------------------+
-#if CFG_TUH_HID_KEYBOARD
-
-#if 0
-#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \
- [0][keycode] = ascii,\
- [1][keycode] = shift_modified,\
-// TODO size of table should be a macro for application to check boundary
-uint8_t const hid_keycode_to_ascii_tbl[2][128] =
+uint8_t tuh_hid_instance_count(uint8_t dev_addr)
{
- HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII)
-};
-#endif
-
-static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
+ return get_dev(dev_addr)->inst_count;
+}
-//------------- KEYBOARD PUBLIC API (parameter validation required) -------------//
-bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr)
+bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance)
{
- return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboardh_data[dev_addr-1].pipe_hdl);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ return (hid_itf->ep_in != 0) || (hid_itf->ep_out != 0);
}
-tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report)
+uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance)
{
- return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ return hid_itf->itf_protocol;
}
-bool tuh_hid_keyboard_is_busy(uint8_t dev_addr)
+uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
{
- return tuh_hid_keyboard_is_mounted(dev_addr) &&
- hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl );
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ return hid_itf->protocol_mode;
}
-#endif
+static bool set_protocol_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ uint8_t const itf_num = (uint8_t) request->wIndex;
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
-//--------------------------------------------------------------------+
-// MOUSE
-//--------------------------------------------------------------------+
-#if CFG_TUH_HID_MOUSE
+ if (XFER_RESULT_SUCCESS == result) hid_itf->protocol_mode = (uint8_t) request->wValue;
-static hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
+ if (tuh_hid_set_protocol_complete_cb)
+ {
+ tuh_hid_set_protocol_complete_cb(dev_addr, instance, hid_itf->protocol_mode);
+ }
-//------------- Public API -------------//
-bool tuh_hid_mouse_is_mounted(uint8_t dev_addr)
-{
- return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(mouseh_data[dev_addr-1].pipe_hdl);
+ return true;
}
-bool tuh_hid_mouse_is_busy(uint8_t dev_addr)
+bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol)
{
- return tuh_hid_mouse_is_mounted(dev_addr) &&
- hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl );
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE);
+
+ TU_LOG2("HID Set Protocol = %d\r\n", protocol);
+
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
+ .wValue = protocol,
+ .wIndex = hid_itf->itf_num,
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, set_protocol_complete) );
+ return true;
}
-tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report)
+static bool set_report_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
{
- return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]);
+ TU_LOG2("HID Set Report complete\r\n");
+
+ if (tuh_hid_set_report_complete_cb)
+ {
+ uint8_t const itf_num = (uint8_t) request->wIndex;
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+
+ uint8_t const report_type = tu_u16_high(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
+
+ tuh_hid_set_report_complete_cb(dev_addr, instance, report_id, report_type, (result == XFER_RESULT_SUCCESS) ? request->wLength : 0);
+ }
+
+ return true;
}
-#endif
+bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
+{
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
-//--------------------------------------------------------------------+
-// GENERIC
-//--------------------------------------------------------------------+
-#if CFG_TUSB_HOST_HID_GENERIC
+ tusb_control_request_t const request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_REPORT,
+ .wValue = tu_u16(report_type, report_id),
+ .wIndex = hid_itf->itf_num,
+ .wLength = len
+ };
-//STATIC_ struct {
-// hidh_interface_info_t
-//} generic_data[CFG_TUSB_HOST_DEVICE_MAX];
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, report, set_report_complete) );
+ return true;
+}
-#endif
+//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance)
+//{
+// TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance));
+//
+// hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+// return !usbh_edpt_busy(dev_addr, hid_itf->ep_in);
+//}
+
+//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len);
//--------------------------------------------------------------------+
-// CLASS-USBH API (don't require to verify parameters)
+// USBH API
//--------------------------------------------------------------------+
void hidh_init(void)
{
-#if CFG_TUH_HID_KEYBOARD
- tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
-#endif
+ tu_memclr(_hidh_dev, sizeof(_hidh_dev));
+}
-#if CFG_TUH_HID_MOUSE
- tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
-#endif
+bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
-#if CFG_TUSB_HOST_HID_GENERIC
- hidh_generic_init();
-#endif
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const instance = get_instance_id_by_epaddr(dev_addr, ep_addr);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+
+ if ( dir == TUSB_DIR_IN )
+ {
+ TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance);
+ TU_LOG1_MEM(hid_itf->epin_buf, 8, 2);
+ tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, xferred_bytes);
+
+ // queue next report
+ hidh_get_report(dev_addr, hid_itf);
+ }else
+ {
+ if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, xferred_bytes);
+ }
+
+ return true;
}
-#if 0
-CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256];
-#endif
+void hidh_close(uint8_t dev_addr)
+{
+ hidh_device_t* hid_dev = get_dev(dev_addr);
+ if (tuh_hid_umount_cb)
+ {
+ for ( uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst);
+ }
+
+ tu_memclr(hid_dev, sizeof(hidh_device_t));
+}
+
+//--------------------------------------------------------------------+
+// Enumeration
+//--------------------------------------------------------------------+
+
+static bool config_set_protocol (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_get_report_desc (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_get_report_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+
+static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
-bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
+bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
{
- uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
+ (void) max_len;
+
+ TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
+
+ // len = interface + hid + n*endpoints
+ uint16_t const drv_len = 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;
//------------- HID descriptor -------------//
- p_desc += p_desc[DESC_OFFSET_LEN];
- tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
- TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA);
+ 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);
+
+ // not enough interface, try to increase CFG_TUH_HID
+ // TODO multiple devices
+ hidh_device_t* hid_dev = get_dev(dev_addr);
+ TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0);
//------------- Endpoint Descriptor -------------//
- p_desc += p_desc[DESC_OFFSET_LEN];
- tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA);
+ p_desc = tu_desc_next(p_desc);
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
- //------------- SET IDLE (0) request -------------//
- tusb_control_request_t request = {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = HID_REQ_CONTROL_SET_IDLE,
- .wValue = 0, // idle_rate = 0
- .wIndex = p_interface_desc->bInterfaceNumber,
- .wLength = 0
- };
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
+ // TODO also open endpoint OUT
+ TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
-#if 0
- //------------- Get Report Descriptor TODO HID parser -------------//
- if ( p_desc_hid->bNumDescriptors )
- {
- STASK_INVOKE(
- usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE),
- TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0,
- p_desc_hid->wReportLength, report_descriptor ),
- error
- );
- (void) error; // if error in getting report descriptor --> treating like there is none
- }
-#endif
+ hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count);
+ hid_dev->inst_count++;
+
+ hid_itf->itf_num = desc_itf->bInterfaceNumber;
+ hid_itf->ep_in = desc_ep->bEndpointAddress;
+ hid_itf->epin_size = desc_ep->wMaxPacketSize.size;
+
+ // Assume bNumDescriptors = 1
+ hid_itf->report_desc_type = desc_hid->bReportType;
+ hid_itf->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
+
+ // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
+ hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
+ if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol;
+
+ return true;
+}
+
+bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass )
+ // Idle rate = 0 mean only report when there is changes
+ uint16_t const idle_rate = 0;
+
+ // SET IDLE request, device can stall if not support this request
+ TU_LOG2("HID Set Idle \r\n");
+ tusb_control_request_t const request =
{
- #if CFG_TUH_HID_KEYBOARD
- if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol)
+ .bmRequestType_bit =
{
- TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) );
- tuh_hid_keyboard_mounted_cb(dev_addr);
- } else
- #endif
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_IDLE,
+ .wValue = idle_rate,
+ .wIndex = itf_num,
+ .wLength = 0
+ };
- #if CFG_TUH_HID_MOUSE
- if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol)
- {
- TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) );
- tuh_hid_mouse_mounted_cb(dev_addr);
- } else
- #endif
+ TU_ASSERT( tuh_control_xfer(dev_addr, &request, NULL, (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc) );
+
+ return true;
+}
+// Force device to work in BOOT protocol
+static bool config_set_protocol(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ // Stall is a valid response for SET_IDLE, therefore we could ignore its result
+ (void) result;
+
+ uint8_t const itf_num = (uint8_t) request->wIndex;
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+
+ TU_LOG2("HID Set Protocol\r\n");
+ hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
{
- // Not supported protocol
- return false;
- }
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
+ .wValue = HID_PROTOCOL_BOOT,
+ .wIndex = hid_itf->itf_num,
+ .wLength = 0
+ };
+
+ TU_ASSERT( tuh_control_xfer(dev_addr, &new_request, NULL, config_get_report_desc) );
+ return true;
+}
+
+static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ // We can be here after SET_IDLE or SET_PROTOCOL (boot device)
+ // Trigger assert if result is not successful with set protocol
+ if ( request->bRequest != HID_REQ_CONTROL_SET_IDLE )
+ {
+ TU_ASSERT(result == XFER_RESULT_SUCCESS);
+ }
+
+ uint8_t const itf_num = (uint8_t) request->wIndex;
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+
+ // Get Report Descriptor if possible
+ // using usbh enumeration buffer since report descriptor can be very long
+ if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
+ {
+ TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len);
+
+ // Driver is mounted without report descriptor
+ config_driver_mount_complete(dev_addr, instance, NULL, 0);
}else
{
- // Not supported subclass
- return false;
+ TU_LOG2("HID Get Report Descriptor\r\n");
+ tusb_control_request_t const new_request =
+ {
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = tu_u16(hid_itf->report_desc_type, 0),
+ .wIndex = itf_num,
+ .wLength = hid_itf->report_desc_len
+ };
+
+ TU_ASSERT(tuh_control_xfer(dev_addr, &new_request, usbh_get_enum_buf(), config_get_report_desc_complete));
}
- *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t);
+ return true;
+}
+
+static bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ TU_ASSERT(XFER_RESULT_SUCCESS == result);
+
+ uint8_t const itf_num = (uint8_t) request->wIndex;
+ uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
+
+ uint8_t const* desc_report = usbh_get_enum_buf();
+ uint16_t const desc_len = request->wLength;
+
+ config_driver_mount_complete(dev_addr, instance, desc_report, desc_len);
return true;
}
-void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
+static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
- (void) xferred_bytes; // TODO may need to use this para later
+ hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+
+ // enumeration is complete
+ tuh_hid_mount_cb(dev_addr, instance, desc_report, desc_len);
+
+ // queue transfer for IN endpoint
+ hidh_get_report(dev_addr, hid_itf);
-#if CFG_TUH_HID_KEYBOARD
- if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
+ // notify usbh that driver enumeration is complete
+ usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num);
+}
+
+//--------------------------------------------------------------------+
+// Report Descriptor Parser
+//--------------------------------------------------------------------+
+
+uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
+{
+ // Report Item 6.2.2.2 USB HID 1.11
+ union TU_ATTR_PACKED
{
- tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event);
- return;
+ uint8_t byte;
+ struct TU_ATTR_PACKED
+ {
+ uint8_t size : 2;
+ uint8_t type : 2;
+ uint8_t tag : 4;
+ };
+ } header;
+
+ tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
+
+ uint8_t report_num = 0;
+ tuh_hid_report_info_t* info = report_info_arr;
+
+ // current parsed report count & size from descriptor
+// uint8_t ri_report_count = 0;
+// uint8_t ri_report_size = 0;
+
+ uint8_t ri_collection_depth = 0;
+
+ while(desc_len && report_num < arr_count)
+ {
+ header.byte = *desc_report++;
+ desc_len--;
+
+ uint8_t const tag = header.tag;
+ uint8_t const type = header.type;
+ uint8_t const size = header.size;
+
+ uint8_t const data8 = desc_report[0];
+
+ TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
+ for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
+ TU_LOG(3, "\r\n");
+
+ switch(type)
+ {
+ case RI_TYPE_MAIN:
+ switch (tag)
+ {
+ case RI_MAIN_INPUT: break;
+ case RI_MAIN_OUTPUT: break;
+ case RI_MAIN_FEATURE: break;
+
+ case RI_MAIN_COLLECTION:
+ ri_collection_depth++;
+ break;
+
+ case RI_MAIN_COLLECTION_END:
+ ri_collection_depth--;
+ if (ri_collection_depth == 0)
+ {
+ info++;
+ report_num++;
+ }
+ break;
+
+ default: break;
+ }
+ break;
+
+ case RI_TYPE_GLOBAL:
+ switch(tag)
+ {
+ case RI_GLOBAL_USAGE_PAGE:
+ // only take in account the "usage page" before REPORT ID
+ if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
+ break;
+
+ case RI_GLOBAL_LOGICAL_MIN : break;
+ case RI_GLOBAL_LOGICAL_MAX : break;
+ case RI_GLOBAL_PHYSICAL_MIN : break;
+ case RI_GLOBAL_PHYSICAL_MAX : break;
+
+ case RI_GLOBAL_REPORT_ID:
+ info->report_id = data8;
+ break;
+
+ case RI_GLOBAL_REPORT_SIZE:
+// ri_report_size = data8;
+ break;
+
+ case RI_GLOBAL_REPORT_COUNT:
+// ri_report_count = data8;
+ break;
+
+ case RI_GLOBAL_UNIT_EXPONENT : break;
+ case RI_GLOBAL_UNIT : break;
+ case RI_GLOBAL_PUSH : break;
+ case RI_GLOBAL_POP : break;
+
+ default: break;
+ }
+ break;
+
+ case RI_TYPE_LOCAL:
+ switch(tag)
+ {
+ case RI_LOCAL_USAGE:
+ // only take in account the "usage" before starting REPORT ID
+ if ( ri_collection_depth == 0 ) info->usage = data8;
+ break;
+
+ case RI_LOCAL_USAGE_MIN : break;
+ case RI_LOCAL_USAGE_MAX : break;
+ case RI_LOCAL_DESIGNATOR_INDEX : break;
+ case RI_LOCAL_DESIGNATOR_MIN : break;
+ case RI_LOCAL_DESIGNATOR_MAX : break;
+ case RI_LOCAL_STRING_INDEX : break;
+ case RI_LOCAL_STRING_MIN : break;
+ case RI_LOCAL_STRING_MAX : break;
+ case RI_LOCAL_DELIMITER : break;
+ default: break;
+ }
+ break;
+
+ // error
+ default: break;
+ }
+
+ desc_report += size;
+ desc_len -= size;
}
-#endif
-#if CFG_TUH_HID_MOUSE
- if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
+ for ( uint8_t i = 0; i < report_num; i++ )
{
- tuh_hid_mouse_isr(pipe_hdl.dev_addr, event);
- return;
+ info = report_info_arr+i;
+ TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
}
-#endif
-#if CFG_TUSB_HOST_HID_GENERIC
+ return report_num;
+}
-#endif
+//--------------------------------------------------------------------+
+// Helper
+//--------------------------------------------------------------------+
+
+// Get Device by address
+TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr)
+{
+ return &_hidh_dev[dev_addr-1];
}
-void hidh_close(uint8_t dev_addr)
+// Get Interface by instance number
+TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance)
{
-#if CFG_TUH_HID_KEYBOARD
- if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) )
- {
- hidh_interface_close(&keyboardh_data[dev_addr-1]);
- tuh_hid_keyboard_unmounted_cb(dev_addr);
- }
-#endif
+ return &_hidh_dev[dev_addr-1].instances[instance];
+}
-#if CFG_TUH_HID_MOUSE
- if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) )
+// Get instance ID by interface number
+static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf)
+{
+ for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ )
{
- hidh_interface_close(&mouseh_data[dev_addr-1]);
- tuh_hid_mouse_unmounted_cb( dev_addr );
+ hidh_interface_t *hid = get_instance(dev_addr, inst);
+
+ if ( (hid->itf_num == itf) && (hid->ep_in || hid->ep_out) ) return inst;
}
-#endif
-#if CFG_TUSB_HOST_HID_GENERIC
- hidh_generic_close(dev_addr);
-#endif
+ return 0xff;
}
+// Get instance ID by endpoint address
+static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr)
+{
+ for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ )
+ {
+ hidh_interface_t *hid = get_instance(dev_addr, inst);
+ if ( (ep_addr == hid->ep_in) || ( ep_addr == hid->ep_out) ) return inst;
+ }
+
+ return 0xff;
+}
#endif
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index a3b614cd7..9a498afec 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -24,14 +24,9 @@
* This file is part of the TinyUSB stack.
*/
-/** \addtogroup ClassDriver_HID
- * @{ */
-
#ifndef _TUSB_HID_HOST_H_
#define _TUSB_HID_HOST_H_
-#include "common/tusb_common.h"
-#include "host/usbh.h"
#include "hid.h"
#ifdef __cplusplus
@@ -39,177 +34,101 @@
#endif
//--------------------------------------------------------------------+
-// KEYBOARD Application API
+// Class Driver Configuration
//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Keyboard Keyboard
- * @{ */
-
-/** \defgroup Keyboard_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
-
-extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled
-
-/** \brief Check if device supports Keyboard interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports Keyboard interface
- * \retval false if device does not support Keyboard interface or is not mounted
- */
-bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr);
-
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report.
- * Alternatively, asynchronous event API can be used
- */
-bool tuh_hid_keyboard_is_busy(uint8_t dev_addr);
-/** \brief Perform a get report from Keyboard interface
- * \param[in] dev_addr device address
- * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
- * \returns \ref tusb_error_t type to indicate success or error condition.
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report);
-
-//------------- Application Callback -------------//
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback
- */
-void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event);
+// TODO Highspeed interrupt can be up to 512 bytes
+#ifndef CFG_TUH_HID_EP_BUFSIZE
+#define CFG_TUH_HID_EP_BUFSIZE 64
+#endif
-/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr);
+typedef struct
+{
+ uint8_t report_id;
+ uint8_t usage;
+ uint16_t usage_page;
-/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr);
-
-/** @} */ // Keyboard_Host
-/** @} */ // ClassDriver_HID_Keyboard
+ // TODO still use the endpoint size for now
+// uint8_t in_len; // length of IN report
+// uint8_t out_len; // length of OUT report
+} tuh_hid_report_info_t;
//--------------------------------------------------------------------+
-// MOUSE Application API
+// Application API
//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Mouse Mouse
- * @{ */
-/** \defgroup Mouse_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
+// Get the number of HID instances
+uint8_t tuh_hid_instance_count(uint8_t dev_addr);
-/** \brief Check if device supports Mouse interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports Mouse interface
- * \retval false if device does not support Mouse interface or is not mounted
- */
-bool tuh_hid_mouse_is_mounted(uint8_t dev_addr);
+// Check if HID instance is mounted
+bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance);
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report.
- * Alternatively, asynchronous event API can be used
- */
-bool tuh_hid_mouse_is_busy(uint8_t dev_addr);
+// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
+uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance);
-/** \brief Perform a get report from Mouse interface
- * \param[in] dev_addr device address
- * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
- * \returns \ref tusb_error_t type to indicate success or error condition.
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report);
+// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+// Note: Device will be initialized in Boot protocol for simplicity.
+// Application can use set_protocol() to switch back to Report protocol.
+uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
-//------------- Application Callback -------------//
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback
- */
-void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event);
+// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
+bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol);
-/** \brief Callback function that will be invoked when a device with Mouse interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_hid_mouse_mounted_cb(uint8_t dev_addr);
+// Set Report using control endpoint
+// report_type is either Intput, Output or Feature, (value from hid_report_type_t)
+bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
-/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr);
+// Parse report descriptor into array of report_info struct and return number of reports.
+// For complicated report, application should write its own parser.
+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;
+
+// Check if the interface is ready to use
+//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance);
-/** @} */ // Mouse_Host
-/** @} */ // ClassDriver_HID_Mouse
+// 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.
+//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len);
//--------------------------------------------------------------------+
-// GENERIC Application API
+// Callbacks (Weak is optional)
//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet)
- * @{ */
-/** \defgroup Generic_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
+// Invoked when device with hid interface is mounted
+// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
+// 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 instance, uint8_t const* report_desc, uint16_t desc_len);
+
+// Invoked when device with hid interface is un-mounted
+TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance);
-bool tuh_hid_generic_is_mounted(uint8_t dev_addr);
-tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
-tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
-tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr);
-tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr);
+// 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 instance, uint8_t const* report, uint16_t len);
-//------------- Application Callback -------------//
-void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event);
+// Invoked when sent report to device successfully via interrupt endpoint
+TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
-/** @} */ // Generic_Host
-/** @} */ // ClassDriver_HID_Generic
+// Invoked when Sent Report to device via either control endpoint
+// len = 0 indicate there is error in the transfer e.g stalled response
+TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
+
+// Invoked when Set Protocol request is complete
+TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t protocol);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-typedef struct {
- pipe_handle_t pipe_hdl;
- uint16_t report_size;
- uint8_t interface_number;
-}hidh_interface_info_t;
-
-void hidh_init(void);
-bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
-void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
-void hidh_close(uint8_t dev_addr);
+void hidh_init (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);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_HOST_H_ */
-
-/** @} */ // ClassDriver_HID
diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h
index f758b6e1c..74dc41749 100644
--- a/src/class/midi/midi.h
+++ b/src/class/midi/midi.h
@@ -61,6 +61,51 @@ typedef enum
MIDI_JACK_EXTERNAL = 0x02
} midi_jack_type_t;
+typedef enum
+{
+ MIDI_CIN_MISC = 0,
+ MIDI_CIN_CABLE_EVENT = 1,
+ MIDI_CIN_SYSCOM_2BYTE = 2, // 2 byte system common message e.g MTC, SongSelect
+ MIDI_CIN_SYSCOM_3BYTE = 3, // 3 byte system common message e.g SPP
+ MIDI_CIN_SYSEX_START = 4, // SysEx starts or continue
+ MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message
+ MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data
+ MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data
+ MIDI_CIN_NOTE_ON = 8,
+ MIDI_CIN_NOTE_OFF = 9,
+ MIDI_CIN_POLY_KEYPRESS = 10,
+ MIDI_CIN_CONTROL_CHANGE = 11,
+ MIDI_CIN_PROGRAM_CHANGE = 12,
+ MIDI_CIN_CHANNEL_PRESSURE = 13,
+ MIDI_CIN_PITCH_BEND_CHANGE = 14,
+ MIDI_CIN_1BYTE_DATA = 15
+} midi_code_index_number_t;
+
+// MIDI 1.0 status byte
+enum
+{
+ //------------- System Exclusive -------------//
+ MIDI_STATUS_SYSEX_START = 0xF0,
+ MIDI_STATUS_SYSEX_END = 0xF7,
+
+ //------------- System Common -------------//
+ MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME = 0xF1,
+ MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER = 0xF2,
+ MIDI_STATUS_SYSCOM_SONG_SELECT = 0xF3,
+ // F4, F5 is undefined
+ MIDI_STATUS_SYSCOM_TUNE_REQUEST = 0xF6,
+
+ //------------- System RealTime -------------//
+ MIDI_STATUS_SYSREAL_TIMING_CLOCK = 0xF8,
+ // 0xF9 is undefined
+ MIDI_STATUS_SYSREAL_START = 0xFA,
+ MIDI_STATUS_SYSREAL_CONTINUE = 0xFB,
+ MIDI_STATUS_SYSREAL_STOP = 0xFC,
+ // 0xFD is undefined
+ MIDI_STATUS_SYSREAL_ACTIVE_SENSING = 0xFE,
+ MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
+};
+
/// MIDI Interface Header Descriptor
typedef struct TU_ATTR_PACKED
{
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index ae6e3afe1..eb85b668c 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -31,19 +31,34 @@
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
-#include "midi_device.h"
-#include "class/audio/audio.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "midi_device.h"
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+
+typedef struct
+{
+ uint8_t buffer[4];
+ uint8_t index;
+ uint8_t total;
+}midid_stream_t;
+
typedef struct
{
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
+ // callers can use the Stream interface with single-byte read/write calls.
+ midid_stream_t stream_write;
+ midid_stream_t stream_read;
+
/*------------- From this point, data is not cleared by bus reset -------------*/
// FIFO
tu_fifo_t rx_ff;
@@ -56,15 +71,9 @@ typedef struct
osal_mutex_def_t tx_ff_mutex;
#endif
- // We need to pack messages into words before queueing their transmission so buffer across write
- // calls.
- uint8_t message_buffer[4];
- uint8_t message_buffer_length;
- uint8_t message_target_length;
-
// Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
} midid_interface_t;
@@ -81,143 +90,279 @@ bool tud_midi_n_mounted (uint8_t itf)
return midi->ep_in && midi->ep_out;
}
+static void _prep_out_transaction (midid_interface_t* p_midi)
+{
+ uint8_t const rhport = TUD_OPT_RHPORT;
+ 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 >= sizeof(p_midi->epout_buf), );
+
+ // 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 >= sizeof(p_midi->epout_buf) ) {
+ usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, sizeof(p_midi->epout_buf));
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_midi->ep_out);
+ }
+}
+
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
-uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id)
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
{
- (void) jack_id;
- return tu_fifo_count(&_midid_itf[itf].rx_ff);
-}
+ (void) cable_num;
-uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize)
-{
- (void) jack_id;
- return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize);
+ midid_interface_t* midi = &_midid_itf[itf];
+ midid_stream_t const* stream = &midi->stream_read;
+
+ // when using with packet API stream total & index are both zero
+ return tu_fifo_count(&midi->rx_ff) + (stream->total - stream->index);
}
-void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id)
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
{
- (void) jack_id;
- tu_fifo_clear(&_midid_itf[itf].rx_ff);
-}
+ (void) cable_num;
+ TU_VERIFY(bufsize, 0);
-void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) {
- if (bufsize % 4 != 0) {
- return;
- }
+ uint8_t* buf8 = (uint8_t*) buffer;
+
+ midid_interface_t* midi = &_midid_itf[itf];
+ midid_stream_t* stream = &midi->stream_read;
+
+ uint32_t total_read = 0;
+ while( bufsize )
+ {
+ // 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;
+
+ uint8_t const code_index = stream->buffer[0] & 0x0f;
+
+ // MIDI 1.0 Table 4-1: Code Index Number Classifications
+ 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;
- for(uint32_t i=0; i<bufsize; i += 4) {
- uint8_t header = buffer[i];
- // uint8_t cable_number = (header & 0xf0) >> 4;
- uint8_t code_index = header & 0x0f;
- // We always copy over the first byte.
- uint8_t count = 1;
- // Ignore subsequent bytes based on the code.
- if (code_index != 0x5 && code_index != 0xf) {
- count = 2;
- if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) {
- count = 3;
+ 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;
+
+ default:
+ stream->total = 3;
+ break;
}
}
- tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count);
+
+ // Copy data up to bufsize
+ uint32_t const count = tu_min32(stream->total - stream->index, bufsize);
+
+ // Skip the header (1st byte) in the buffer
+ memcpy(buf8, stream->buffer + 1 + stream->index, count);
+
+ total_read += count;
+ stream->index += count;
+ buf8 += count;
+ bufsize -= count;
+
+ // complete current event packet, reset stream
+ if ( stream->total == stream->index )
+ {
+ stream->index = 0;
+ stream->total = 0;
+ }
}
+
+ 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);
+
+ uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
+ _prep_out_transaction(midi);
+ return (num_read == 4);
+}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
-static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index)
+static uint32_t write_flush(midid_interface_t* midi)
{
- (void) itf_index;
+ // No data to send
+ if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
// skip if previous transfer not complete
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) );
+ TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
+
+ uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
- uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE);
- if (count > 0)
+ if (count)
{
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
+ TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
+ return count;
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, midi->ep_in);
+ return 0;
}
- return true;
}
-uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
{
midid_interface_t* midi = &_midid_itf[itf];
- if (midi->itf_num == 0) {
- return 0;
- }
+ TU_VERIFY(midi->ep_in, 0);
+
+ midid_stream_t* stream = &midi->stream_write;
uint32_t i = 0;
- while (i < bufsize) {
- uint8_t data = buffer[i];
- if (midi->message_buffer_length == 0) {
- uint8_t msg = data >> 4;
- midi->message_buffer[1] = data;
- midi->message_buffer_length = 2;
- // Check to see if we're still in a SysEx transmit.
- if (midi->message_buffer[0] == 0x4) {
- if (data == 0xf7) {
- midi->message_buffer[0] = 0x5;
- } else {
- midi->message_buffer_length = 4;
- }
- } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
- midi->message_buffer[0] = jack_id << 4 | msg;
- midi->message_target_length = 4;
- } else if (msg == 0xC || msg == 0xD) {
- midi->message_buffer[0] = jack_id << 4 | msg;
- midi->message_target_length = 3;
- } else if (msg == 0xf) {
- if (data == 0xf0) {
- midi->message_buffer[0] = 0x4;
- midi->message_target_length = 4;
- } else if (data == 0xf1 || data == 0xf3) {
- midi->message_buffer[0] = 0x2;
- midi->message_target_length = 3;
- } else if (data == 0xf2) {
- midi->message_buffer[0] = 0x3;
- midi->message_target_length = 4;
- } else {
- midi->message_buffer[0] = 0x5;
- midi->message_target_length = 2;
- }
- } else {
- // Pack individual bytes if we don't support packing them into words.
- midi->message_buffer[0] = jack_id << 4 | 0xf;
- midi->message_buffer[2] = 0;
- midi->message_buffer[3] = 0;
- midi->message_buffer_length = 2;
- midi->message_target_length = 2;
+ while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
+ {
+ uint8_t const data = buffer[i];
+ i++;
+
+ if ( stream->index == 0 )
+ {
+ //------------- New event packet -------------//
+
+ uint8_t const msg = data >> 4;
+
+ stream->index = 2;
+ stream->buffer[1] = data;
+
+ // Check to see if we're still in a SysEx transmit.
+ if ( stream->buffer[0] == MIDI_CIN_SYSEX_START )
+ {
+ if ( data == MIDI_STATUS_SYSEX_END )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->total = 2;
+ }
+ else
+ {
+ stream->total = 4;
+ }
+ }
+ else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
+ {
+ // Channel Voice Messages
+ stream->buffer[0] = (cable_num << 4) | msg;
+ stream->total = 4;
+ }
+ else if ( msg == 0xf )
+ {
+ // System message
+ if ( data == MIDI_STATUS_SYSEX_START )
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_START;
+ stream->total = 4;
}
- } else {
- midi->message_buffer[midi->message_buffer_length] = data;
- midi->message_buffer_length += 1;
- // See if this byte ends a SysEx.
- if (midi->message_buffer[0] == 0x4 && data == 0xf7) {
- midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1);
- midi->message_target_length = midi->message_buffer_length;
+ 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->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
+ stream->total = 4;
+ }
+ else
+ {
+ stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->total = 2;
+ }
+ }
+ else
+ {
+ // Pack individual bytes if we don't support packing them into words.
+ stream->buffer[0] = cable_num << 4 | 0xf;
+ stream->buffer[2] = 0;
+ stream->buffer[3] = 0;
+ stream->index = 2;
+ stream->total = 2;
+ }
}
+ else
+ {
+ //------------- On-going (buffering) packet -------------//
- if (midi->message_buffer_length == midi->message_target_length) {
- uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4);
- if (written < 4) {
- TU_ASSERT( written == 0 );
- break;
- }
- midi->message_buffer_length = 0;
+ TU_ASSERT(stream->index < 4, i);
+ 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);
+ stream->total = stream->index;
+ }
+ }
+
+ // Send out packet
+ if ( stream->index == stream->total )
+ {
+ // zeroes unused bytes
+ for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0;
+
+ uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+
+ // complete current event packet, reset stream
+ stream->index = stream->total = 0;
+
+ // FIFO overflown, since we already check fifo remaining. It is probably race condition
+ TU_ASSERT(count == 4, i);
}
- i++;
}
- maybe_transmit(midi, itf);
+
+ write_flush(midi);
return i;
}
+bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
+{
+ midid_interface_t* midi = &_midid_itf[itf];
+ TU_VERIFY(midi->ep_in);
+
+ if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
+
+ tu_fifo_write_n(&midi->tx_ff, packet, 4);
+ write_flush(midi);
+
+ return true;
+}
+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
@@ -230,12 +375,12 @@ void midid_init(void)
midid_interface_t* midi = &_midid_itf[i];
// config fifo
- tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true);
- tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true);
+ 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_FIFO_MUTEX
- tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
- tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
+ tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
+ tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
#endif
}
}
@@ -253,21 +398,30 @@ void midid_reset(uint8_t rhport)
}
}
-bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length)
+uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
- // For now handle the audio control interface as well.
- if ( AUDIO_SUBCLASS_CONTROL == p_interface_desc->bInterfaceSubClass) {
- uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
+ // 1st Interface is Audio Control v1
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
+
+ uint16_t drv_len = tu_desc_len(desc_itf);
+ uint8_t const * p_desc = tu_desc_next(desc_itf);
- // Skip over the class specific descriptor.
- (*p_length) += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
- return true;
+ // 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);
}
- TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == p_interface_desc->bInterfaceSubClass &&
- AUDIO_PROTOCOL_V1 == p_interface_desc->bInterfaceProtocol );
+ // 2nd Interface is MIDI Streaming
+ TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+ tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
+
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
+ AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
// Find available interface
midid_interface_t * p_midi = NULL;
@@ -280,49 +434,54 @@ bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc,
}
}
- p_midi->itf_num = p_interface_desc->bInterfaceNumber;
+ p_midi->itf_num = desc_midi->bInterfaceNumber;
+ (void) p_midi->itf_num;
- uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
+ // next descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ // Find and open endpoint descriptors
uint8_t found_endpoints = 0;
- while (found_endpoints < p_interface_desc->bNumEndpoints)
+ while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
{
- if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
- TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false);
- uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- p_midi->ep_in = ep_addr;
- } else {
- p_midi->ep_out = ep_addr;
- }
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- found_endpoints += 1;
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
+ {
+ p_midi->ep_in = ep_addr;
+ } else {
+ p_midi->ep_out = ep_addr;
+ }
+
+ // Class Specific MIDI Stream endpoint descriptor
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+
+ found_endpoints += 1;
}
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
// Prepare for incoming data
- TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
+ _prep_out_transaction(p_midi);
- return true;
+ return drv_len;
}
-bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
(void) rhport;
- (void) p_request;
- return true;
-}
-
-bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- (void) rhport;
- (void) p_request;
+ (void) stage;
+ (void) request;
// driver doesn't support any request yet
return false;
@@ -331,29 +490,47 @@ bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_requ
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 itf = 0;
- midid_interface_t* p_midi = _midid_itf;
+ uint8_t itf;
+ midid_interface_t* p_midi;
- for ( ; ; itf++, p_midi++)
+ // Identify which interface to use
+ for (itf = 0; itf < CFG_TUD_MIDI; itf++)
{
- if (itf >= TU_ARRAY_SIZE(_midid_itf)) return false;
-
- if ( ep_addr == p_midi->ep_out ) break;
+ p_midi = &_midid_itf[itf];
+ if ( ( ep_addr == p_midi->ep_out ) || ( ep_addr == p_midi->ep_in ) ) break;
}
+ TU_ASSERT(itf < CFG_TUD_MIDI);
// receive new data
if ( ep_addr == p_midi->ep_out )
{
- midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes);
+ tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, xferred_bytes);
+
+ // invoke receive callback if available
+ if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
// prepare for next
- TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
- } else if ( ep_addr == p_midi->ep_in ) {
- maybe_transmit(p_midi, itf);
+ // 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(p_midi);
+ }
+ else if ( ep_addr == p_midi->ep_in )
+ {
+ if (0 == write_flush(p_midi))
+ {
+ // 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);
+ }
+ }
+ }
}
-
- // nothing to do with in and notif endpoint
return true;
}
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index fbf4bce0b..211edc8d1 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -27,17 +27,20 @@
#ifndef _TUSB_MIDI_DEVICE_H_
#define _TUSB_MIDI_DEVICE_H_
-#include "common/tusb_common.h"
-#include "device/usbd.h"
-
#include "class/audio/audio.h"
#include "midi.h"
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
-#ifndef CFG_TUD_MIDI_EPSIZE
-#define CFG_TUD_MIDI_EPSIZE 64
+
+#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
+#endif
+
+#ifndef CFG_TUD_MIDI_EP_BUFSIZE
+ #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifdef __cplusplus
@@ -53,24 +56,64 @@
// Application API (Multiple Interfaces)
// CFG_TUD_MIDI > 1
//--------------------------------------------------------------------+
-bool tud_midi_n_mounted (uint8_t itf);
-uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id);
-uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize);
-void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id);
-uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
-static inline
-uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3);
+// Check if midi interface is mounted
+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);
+
+// 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 event packet (4 bytes)
+bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4]);
+
+// Write event packet (4 bytes)
+bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]);
//--------------------------------------------------------------------+
-// Application API (Interface0)
+// Application API (Single Interface)
//--------------------------------------------------------------------+
-static inline bool tud_midi_mounted (void);
-static inline uint32_t tud_midi_available (void);
-static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize);
-static inline void tud_midi_read_flush (void);
-static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
-static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3);
+static inline bool tud_midi_mounted (void);
+static inline uint32_t tud_midi_available (void);
+
+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);
+
+static inline bool tud_midi_packet_read (uint8_t packet[4]);
+static inline bool tud_midi_packet_write (uint8_t const packet[4]);
+
+//------------- Deprecated API name -------------//
+// TODO remove after 0.10.0 release
+
+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);
+}
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
@@ -81,12 +124,6 @@ TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
// Inline Functions
//--------------------------------------------------------------------+
-static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3)
-{
- uint8_t msg[3] = { b1, b2, b3 };
- return tud_midi_n_write(itf, jack_id, msg, 3);
-}
-
static inline bool tud_midi_mounted (void)
{
return tud_midi_n_mounted(0);
@@ -97,36 +134,34 @@ static inline uint32_t tud_midi_available (void)
return tud_midi_n_available(0, 0);
}
-static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize)
+static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
{
- return tud_midi_n_read(0, 0, buffer, bufsize);
+ return tud_midi_n_stream_read(0, 0, buffer, bufsize);
}
-static inline void tud_midi_read_flush (void)
+static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
{
- tud_midi_n_read_flush(0, 0);
+ return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
}
-static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
+static inline bool tud_midi_packet_read (uint8_t packet[4])
{
- return tud_midi_n_write(0, jack_id, buffer, bufsize);
+ return tud_midi_n_packet_read(0, packet);
}
-static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3)
+static inline bool tud_midi_packet_write (uint8_t const packet[4])
{
- uint8_t msg[3] = { b1, b2, b3 };
- return tud_midi_write(jack_id, msg, 3);
+ return tud_midi_n_packet_write(0, packet);
}
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void midid_init (void);
-void midid_reset (uint8_t rhport);
-bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool midid_control_complete (uint8_t rhport, 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);
+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);
#ifdef __cplusplus
}
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index cba8066b7..84b6e4d79 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -24,13 +24,6 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup group_class
- * \defgroup ClassDriver_MSC MassStorage (MSC)
- * @{ */
-
-/** \defgroup ClassDriver_MSC_Common Common Definitions
- * @{ */
-
#ifndef _TUSB_MSC_H_
#define _TUSB_MSC_H_
@@ -255,7 +248,7 @@ typedef struct TU_ATTR_PACKED
uint8_t : 3;
uint8_t disable_block_descriptor : 1;
- uint8_t : 0;
+ uint8_t : 4;
uint8_t page_code : 6;
uint8_t page_control : 2;
@@ -387,6 +380,3 @@ TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
#endif
#endif /* _TUSB_MSC_H_ */
-
-/// @}
-/// @}
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 3ff0eba92..014e8b69e 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -28,13 +28,19 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC)
-#include "common/tusb_common.h"
-#include "msc_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "device/dcd.h" // for faking dcd_event_xfer_complete
+
+#include "msc_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+
+// Can be selectively disabled to reduce logging when troubleshooting other driver
+#define MSC_DEBUG 2
+
enum
{
MSC_STAGE_CMD = 0,
@@ -65,36 +71,30 @@ typedef struct
}mscd_interface_t;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static inline uint32_t rdwr10_get_lba(uint8_t const command[])
{
- // read10 & write10 has the same format
- scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
-
- // copy first to prevent mis-aligned access
- uint32_t lba;
- memcpy(&lba, &p_rdwr10->lba, 4);
+ // use offsetof to avoid pointer to the odd/unaligned address
+ uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
- // lba is in Big Endian format
+ // lba is in Big Endian
return tu_ntohl(lba);
}
static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
{
- // read10 & write10 has the same format
- scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
-
- // copy first to prevent mis-aligned access
- uint16_t block_count;
- memcpy(&block_count, &p_rdwr10->block_count, 2);
+ // use offsetof to avoid pointer to the odd/misaligned address
+ uint16_t const block_count = tu_unaligned_read16(command + offsetof(scsi_write10_t, block_count));
+ // block count is in Big Endian
return tu_ntohs(block_count);
}
@@ -103,7 +103,7 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
-static lookup_entry_t const _msc_scsi_cmd_lookup[] =
+TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
@@ -118,7 +118,7 @@ static lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
-static lookup_table_t const _msc_scsi_cmd_table =
+TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
{
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
@@ -154,30 +154,43 @@ void mscd_reset(uint8_t rhport)
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
-bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
+uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// only support SCSI's BOT protocol
- TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
+ TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
+ MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
- mscd_interface_t * p_msc = &_mscd_itf;
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
- // Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
+ // Max length mus be at least 1 interface + 2 endpoints
+ TU_ASSERT(max_len >= drv_len, 0);
+ mscd_interface_t * p_msc = &_mscd_itf;
p_msc->itf_num = itf_desc->bInterfaceNumber;
- (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+
+ // Open endpoint pair
+ TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
// Prepare for Command Block Wrapper
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
+ if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
+ {
+ TU_LOG_FAILED();
+ TU_BREAKPOINT();
+ }
- return true;
+ return drv_len;
}
-// Handle class control request
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request)
{
+ // nothing to do with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
// Handle class request only
TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
@@ -207,190 +220,6 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
return true;
}
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
- (void) request;
-
- // nothing to do
- return true;
-}
-
-// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
-// In case of a failed status, sense key must be set for reason of failure
-int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
- (void) bufsize; // TODO refractor later
- int32_t resplen;
-
- switch ( scsi_cmd[0] )
- {
- case SCSI_CMD_TEST_UNIT_READY:
- resplen = 0;
- if ( !tud_msc_test_unit_ready_cb(lun) )
- {
- // Failed status response
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- break;
-
- case SCSI_CMD_START_STOP_UNIT:
- resplen = 0;
-
- if (tud_msc_start_stop_cb)
- {
- 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
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- }
- break;
-
- case SCSI_CMD_READ_CAPACITY_10:
- {
- uint32_t block_count;
- uint32_t block_size;
- uint16_t block_size_u16;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
- block_size = (uint32_t) block_size_u16;
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- scsi_read_capacity10_resp_t read_capa10;
-
- read_capa10.last_lba = tu_htonl(block_count-1);
- read_capa10.block_size = tu_htonl(block_size);
-
- resplen = sizeof(read_capa10);
- memcpy(buffer, &read_capa10, resplen);
- }
- }
- break;
-
- case SCSI_CMD_READ_FORMAT_CAPACITY:
- {
- scsi_read_format_capacity_data_t read_fmt_capa =
- {
- .list_length = 8,
- .block_num = 0,
- .descriptor_type = 2, // formatted media
- .block_size_u16 = 0
- };
-
- uint32_t block_count;
- uint16_t block_size;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size);
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- read_fmt_capa.block_num = tu_htonl(block_count);
- read_fmt_capa.block_size_u16 = tu_htons(block_size);
-
- resplen = sizeof(read_fmt_capa);
- memcpy(buffer, &read_fmt_capa, resplen);
- }
- }
- break;
-
- case SCSI_CMD_INQUIRY:
- {
- scsi_inquiry_resp_t inquiry_rsp =
- {
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- };
-
- // vendor_id, product_id, product_rev is space padded string
- memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
- memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
- memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
-
- tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
-
- resplen = sizeof(inquiry_rsp);
- memcpy(buffer, &inquiry_rsp, resplen);
- }
- break;
-
- case SCSI_CMD_MODE_SENSE_6:
- {
- scsi_mode_sense6_resp_t mode_resp =
- {
- .data_len = 3,
- .medium_type = 0,
- .write_protected = false,
- .reserved = 0,
- .block_descriptor_len = 0 // no block descriptor are included
- };
-
- bool writable = true;
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(lun);
- }
- mode_resp.write_protected = !writable;
-
- resplen = sizeof(mode_resp);
- memcpy(buffer, &mode_resp, resplen);
- }
- break;
-
- case SCSI_CMD_REQUEST_SENSE:
- {
- scsi_sense_fixed_resp_t sense_rsp =
- {
- .response_code = 0x70,
- .valid = 1
- };
-
- sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
-
- sense_rsp.sense_key = _mscd_itf.sense_key;
- sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
- sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
-
- resplen = sizeof(sense_rsp);
- memcpy(buffer, &sense_rsp, resplen);
-
- // Clear sense data after copy
- tud_msc_set_sense(lun, 0, 0, 0);
- }
- break;
-
- default: resplen = -1; break;
- }
-
- return resplen;
-}
-
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
@@ -407,8 +236,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_ASSERT( event == XFER_RESULT_SUCCESS &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
- TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
- // TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
+ // TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
@@ -480,8 +309,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
case MSC_STAGE_DATA:
- TU_LOG2(" SCSI Data\n");
- //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Data\r\n");
+ //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
// OUT transfer, invoke callback if needed
if ( !tu_bit_test(p_cbw->dir, 7) )
@@ -553,7 +382,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
else
{
// READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
- // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE
+ // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
@@ -577,8 +406,26 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Wait for the Status phase to complete
if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
{
- TU_LOG2(" SCSI Status: %u\n", p_csw->status);
- // TU_LOG2_MEM(p_csw, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Status: %u\r\n", p_csw->status);
+ // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
+
+ // Invoke complete callback if defined
+ // Note: There is racing issue with samd51 + qspi flash testing with arduino
+ // if complete_cb() is invoked after queuing the status.
+ switch(p_cbw->command[0])
+ {
+ case SCSI_CMD_READ_10:
+ if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
+ break;
+
+ case SCSI_CMD_WRITE_10:
+ if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
+ break;
+
+ default:
+ if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
+ break;
+ }
// Move to default CMD stage
p_msc->stage = MSC_STAGE_CMD;
@@ -603,22 +450,6 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
}
else
{
- // Invoke complete callback if defined
- switch(p_cbw->command[0])
- {
- case SCSI_CMD_READ_10:
- if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
- break;
-
- case SCSI_CMD_WRITE_10:
- if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
- break;
-
- default:
- if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
- break;
- }
-
// Move to Status Sent stage
p_msc->stage = MSC_STAGE_STATUS_SENT;
@@ -633,6 +464,180 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
/*------------------------------------------------------------------*/
/* SCSI Command Process
*------------------------------------------------------------------*/
+
+// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
+// In case of a failed status, sense key must be set for reason of failure
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
+{
+ (void) bufsize; // TODO refractor later
+ int32_t resplen;
+
+ switch ( scsi_cmd[0] )
+ {
+ case SCSI_CMD_TEST_UNIT_READY:
+ resplen = 0;
+ if ( !tud_msc_test_unit_ready_cb(lun) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ break;
+
+ case SCSI_CMD_START_STOP_UNIT:
+ resplen = 0;
+
+ if (tud_msc_start_stop_cb)
+ {
+ 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
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_CAPACITY_10:
+ {
+ uint32_t block_count;
+ uint32_t block_size;
+ uint16_t block_size_u16;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
+ block_size = (uint32_t) block_size_u16;
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ scsi_read_capacity10_resp_t read_capa10;
+
+ read_capa10.last_lba = tu_htonl(block_count-1);
+ read_capa10.block_size = tu_htonl(block_size);
+
+ resplen = sizeof(read_capa10);
+ memcpy(buffer, &read_capa10, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_FORMAT_CAPACITY:
+ {
+ scsi_read_format_capacity_data_t read_fmt_capa =
+ {
+ .list_length = 8,
+ .block_num = 0,
+ .descriptor_type = 2, // formatted media
+ .block_size_u16 = 0
+ };
+
+ uint32_t block_count;
+ uint16_t block_size;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size);
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ read_fmt_capa.block_num = tu_htonl(block_count);
+ read_fmt_capa.block_size_u16 = tu_htons(block_size);
+
+ resplen = sizeof(read_fmt_capa);
+ memcpy(buffer, &read_fmt_capa, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_INQUIRY:
+ {
+ scsi_inquiry_resp_t inquiry_rsp =
+ {
+ .is_removable = 1,
+ .version = 2,
+ .response_data_format = 2,
+ };
+
+ // vendor_id, product_id, product_rev is space padded string
+ memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
+ memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
+ memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
+
+ tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
+
+ resplen = sizeof(inquiry_rsp);
+ memcpy(buffer, &inquiry_rsp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_MODE_SENSE_6:
+ {
+ scsi_mode_sense6_resp_t mode_resp =
+ {
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
+ };
+
+ bool writable = true;
+ if (tud_msc_is_writable_cb) {
+ writable = tud_msc_is_writable_cb(lun);
+ }
+ mode_resp.write_protected = !writable;
+
+ resplen = sizeof(mode_resp);
+ memcpy(buffer, &mode_resp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_REQUEST_SENSE:
+ {
+ scsi_sense_fixed_resp_t sense_rsp =
+ {
+ .response_code = 0x70,
+ .valid = 1
+ };
+
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+
+ sense_rsp.sense_key = _mscd_itf.sense_key;
+ sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
+ sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
+
+ resplen = sizeof(sense_rsp);
+ memcpy(buffer, &sense_rsp, resplen);
+
+ // Clear sense data after copy
+ tud_msc_set_sense(lun, 0, 0, 0);
+ }
+ break;
+
+ default: resplen = -1; break;
+ }
+
+ return resplen;
+}
+
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 66180777e..8f90ef4ad 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -28,7 +28,6 @@
#define _TUSB_MSC_DEVICE_H_
#include "common/tusb_common.h"
-#include "device/usbd.h"
#include "msc.h"
#ifdef __cplusplus
@@ -38,59 +37,58 @@
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
-TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
-#ifndef CFG_TUD_MSC_BUFSIZE
- #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
+#if !defined(CFG_TUD_MSC_EP_BUFSIZE) & defined(CFG_TUD_MSC_BUFSIZE)
+ // TODO warn user to use new name later on
+ // #warning CFG_TUD_MSC_BUFSIZE is renamed to CFG_TUD_MSC_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_MSC_EP_BUFSIZE CFG_TUD_MSC_BUFSIZE
#endif
-/** \addtogroup ClassDriver_MSC
- * @{
- * \defgroup MSC_Device Device
- * @{ */
+#ifndef CFG_TUD_MSC_EP_BUFSIZE
+ #error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better
+#endif
+
+TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+// Set SCSI sense response
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
//--------------------------------------------------------------------+
// Application Callbacks (WEAK is optional)
//--------------------------------------------------------------------+
-/**
- * Invoked when received \ref SCSI_CMD_READ_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be read
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which application need to update with the response data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer
- * this amount first and invoked this again for remaining data.
- *
- * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete.
- * tinyusb will invoke this callback with the same parameters again some time later.
- *
- * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
+// Invoked when received SCSI READ10 command
+// - Address = lba * BLOCK_SIZE + offset
+// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
+//
+// - Application fill the buffer (up to bufsize) with address contents and return number of read byte. If
+// - read < bufsize : These bytes are transferred first and callback invoked again for remaining data.
+//
+// - read == 0 : Indicate application is not ready yet e.g disk I/O busy.
+// Callback invoked again with the same parameters later on.
+//
+// - read < 0 : Indicate application error e.g invalid address. This request will be STALLed
+// and return failed status in command status wrapper phase.
int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
-/**
- * Invoked when received \ref SCSI_CMD_WRITE_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be write
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which holds written data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with
- * other work and invoked this again with adjusted parameters.
- *
- * \retval zero Indicate application is not ready yet e.g disk I/O is not complete.
- * Tinyusb will invoke this callback with the same parameters again some time later.
- *
- * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
+// Invoked when received SCSI WRITE10 command
+// - Address = lba * BLOCK_SIZE + offset
+// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
+//
+// - Application write data from buffer to address contents (up to bufsize) and return number of written byte. If
+// - write < bufsize : callback invoked again with remaining data later on.
+//
+// - write == 0 : Indicate application is not ready yet e.g disk I/O busy.
+// Callback invoked again with the same parameters later on.
+//
+// - write < 0 : Indicate application error e.g invalid address. This request will be STALLed
+// and return failed status in command status wrapper phase.
+//
+// TODO change buffer to const uint8_t*
int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
// Invoked when received SCSI_CMD_INQUIRY
@@ -145,18 +143,14 @@ TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[1
// Hook to make a mass storage device read-only. TODO remove
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
-/** @} */
-/** @} */
-
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void mscd_init (void);
-void mscd_reset (uint8_t rhport);
-bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void mscd_init (void);
+void mscd_reset (uint8_t rhport);
+uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
+bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index e20969f57..f02f4550a 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -28,272 +28,354 @@
#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "host/usbh_classdriver.h"
+
#include "msc_host.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
+enum
+{
+ MSC_STAGE_IDLE = 0,
+ MSC_STAGE_CMD,
+ MSC_STAGE_DATA,
+ MSC_STAGE_STATUS,
+};
-//------------- Initalization Data -------------//
-static osal_semaphore_def_t msch_sem_def;
-static osal_semaphore_t msch_sem_hdl;
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
-// buffer used to read scsi information when mounted, largest response data currently is inquiry
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
+ uint8_t max_lun;
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+ volatile bool configured; // Receive SET_CONFIGURE
+ volatile bool mounted; // Enumeration is complete
+
+ struct {
+ uint32_t block_size;
+ uint32_t block_count;
+ } capacity[CFG_TUH_MSC_MAXLUN];
+
+ //------------- SCSI -------------//
+ uint8_t stage;
+ void* buffer;
+ tuh_msc_complete_cb_t complete_cb;
+
+ msc_cbw_t cbw;
+ msc_csw_t csw;
+}msch_interface_t;
+
+CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUSB_HOST_DEVICE_MAX];
+
+// buffer used to read scsi information when mounted
+// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4)
+static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
+
+static inline msch_interface_t* get_itf(uint8_t dev_addr)
+{
+ return &_msch_itf[dev_addr-1];
+}
//--------------------------------------------------------------------+
// PUBLIC API
//--------------------------------------------------------------------+
-bool tuh_msc_is_mounted(uint8_t dev_addr)
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
{
- return tuh_device_is_configured(dev_addr) && // is configured can be omitted
- msch_data[dev_addr-1].is_initialized;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->max_lun;
}
-bool tuh_msc_is_busy(uint8_t dev_addr)
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
{
- return msch_data[dev_addr-1].is_initialized &&
- hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in);
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_count;
}
-uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr)
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_size;
}
-uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr)
+bool tuh_msc_mounted(uint8_t dev_addr)
{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted;
}
-tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size)
+bool tuh_msc_ready(uint8_t dev_addr)
{
- if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED;
- TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA);
-
- (*p_last_lba) = msch_data[dev_addr-1].last_lba;
- (*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size;
-
- return TUSB_ERROR_NONE;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in);
}
//--------------------------------------------------------------------+
// PUBLIC API: SCSI COMMAND
//--------------------------------------------------------------------+
-static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun)
+static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
{
- p_cbw->signature = MSC_CBW_SIGNATURE;
- p_cbw->tag = 0xCAFECAFE;
- p_cbw->lun = lun;
+ tu_memclr(cbw, sizeof(msc_cbw_t));
+ cbw->signature = MSC_CBW_SIGNATURE;
+ cbw->tag = 0x54555342; // TUSB
+ cbw->lun = lun;
}
-static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer)
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb)
{
- if ( NULL != p_buffer)
- { // there is data phase
- if (p_msch->cbw.dir & TUSB_DIR_IN_MASK)
- {
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED );
- }else
- {
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED );
- }
- }
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED);
+ // TODO claim endpoint
- return TUSB_ERROR_NONE;
-}
+ p_msc->cbw = *cbw;
+ p_msc->stage = MSC_STAGE_CMD;
+ p_msc->buffer = data;
+ p_msc->complete_cb = complete_cb;
-tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t);
+ return true;
+}
- //------------- SCSI command -------------//
- scsi_inquiry_t cmd_inquiry =
- {
- .cmd_code = SCSI_CMD_INQUIRY,
- .alloc_length = sizeof(scsi_inquiry_resp_t)
- };
+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)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
+ cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read_capacity10_t);
+ cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
}
-tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- //------------- SCSI command -------------//
- scsi_read_capacity10_t cmd_read_capacity10 =
+ cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_inquiry_t);
+
+ scsi_inquiry_t const cmd_inquiry =
{
- .cmd_code = SCSI_CMD_READ_CAPACITY_10,
- .lba = 0,
- .partial_medium_indicator = 0
+ .cmd_code = SCSI_CMD_INQUIRY,
+ .alloc_length = sizeof(scsi_inquiry_resp_t)
};
+ memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+}
+
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
+ cbw.total_bytes = 0;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+ cbw.command[1] = lun; // according to wiki TODO need verification
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb);
}
-tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb)
{
- (void) lun; // TODO [MSCH] multiple lun support
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ cbw.total_bytes = 18; // TODO sense response
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_request_sense_t);
- //------------- Command Block Wrapper -------------//
- p_msch->cbw.total_bytes = 18;
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t);
-
- //------------- SCSI command -------------//
- scsi_request_sense_t cmd_request_sense =
+ scsi_request_sense_t const cmd_request_sense =
{
- .cmd_code = SCSI_CMD_REQUEST_SENSE,
- .alloc_length = 18
+ .cmd_code = SCSI_CMD_REQUEST_SENSE,
+ .alloc_length = 18
};
- memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
+ memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, resposne, complete_cb);
}
-tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw)
+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)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read10_t);
+
+ scsi_read10_t const cmd_read10 =
+ {
+ .cmd_code = SCSI_CMD_READ_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
+ };
+
+ memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb);
+}
+
+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)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- p_msch->cbw.total_bytes = 0; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_OUT;
- p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_write10_t);
- //------------- SCSI command -------------//
- scsi_test_unit_ready_t cmd_test_unit_ready =
+ scsi_write10_t const cmd_write10 =
{
- .cmd_code = SCSI_CMD_TEST_UNIT_READY,
- .lun = lun // according to wiki
+ .cmd_code = SCSI_CMD_WRITE_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
- memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len);
-
- // TODO MSCH refractor test uinit ready
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED );
+ memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, (void*) buffer, complete_cb);
}
-tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count)
+#if 0
+// MSC interface Reset (not used now)
+bool tuh_msc_reset(uint8_t dev_addr)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
-
- p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_read10_t);
-
- //------------- SCSI command -------------//
- scsi_read10_t cmd_read10 =
+ tusb_control_request_t const new_request =
{
- .cmd_code = SCSI_CMD_READ_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = MSC_REQ_RESET,
+ .wValue = 0,
+ .wIndex = p_msc->itf_num,
+ .wLength = 0
};
+ TU_ASSERT( usbh_control_xfer( dev_addr, &new_request, NULL ) );
+}
+#endif
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API
+//--------------------------------------------------------------------+
+void msch_init(void)
+{
+ tu_memclr(_msch_itf, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
+}
- memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len);
+void msch_close(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer));
+ // invoke Application Callback
+ if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
- return TUSB_ERROR_NONE;
+ tu_memclr(p_msc, sizeof(msch_interface_t));
}
-tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count)
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ msc_cbw_t const * cbw = &p_msc->cbw;
+ msc_csw_t * csw = &p_msc->csw;
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
+ switch (p_msc->stage)
+ {
+ case MSC_STAGE_CMD:
+ // Must be Command Block
+ TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
- p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_OUT;
- p_msch->cbw.cmd_len = sizeof(scsi_write10_t);
+ if ( cbw->total_bytes && p_msc->buffer )
+ {
+ // Data stage if any
+ p_msc->stage = MSC_STAGE_DATA;
- //------------- SCSI command -------------//
- scsi_write10_t cmd_write10 =
- {
- .cmd_code = SCSI_CMD_WRITE_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
- };
+ uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, cbw->total_bytes));
+ }else
+ {
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ }
+ break;
- memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len);
+ case MSC_STAGE_DATA:
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ break;
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer));
+ case MSC_STAGE_STATUS:
+ // SCSI op is complete
+ p_msc->stage = MSC_STAGE_IDLE;
- return TUSB_ERROR_NONE;
+ if (p_msc->complete_cb) p_msc->complete_cb(dev_addr, cbw, csw);
+ break;
+
+ // unknown state
+ default: break;
+ }
+
+ return true;
}
//--------------------------------------------------------------------+
-// CLASS-USBH API (don't require to verify parameters)
+// MSC Enumeration
//--------------------------------------------------------------------+
-void msch_init(void)
-{
- tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
- msch_sem_hdl = osal_semaphore_create(&msch_sem_def);
-}
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
{
- TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
+ TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ TU_ASSERT(drv_len <= max_len);
- //------------- Open Data Pipe -------------//
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+ 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);
for(uint32_t i=0; i<2; i++)
{
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
- TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
-
- TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
{
@@ -306,108 +388,101 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
}
- p_msc->itf_numr = itf_desc->bInterfaceNumber;
- (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+ p_msc->itf_num = desc_itf->bInterfaceNumber;
- //------------- Get Max Lun -------------//
- tusb_control_request_t request = {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
- .bRequest = MSC_REQ_GET_MAX_LUN,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 1
- };
- // TODO STALL means zero
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) );
- p_msc->max_lun = msch_buffer[0];
-
-#if 0
- //------------- Reset -------------//
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = MSC_REQ_RESET,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 0
- };
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
-#endif
-
- enum { SCSI_XFER_TIMEOUT = 2000 };
- //------------- SCSI Inquiry -------------//
- tusbh_msc_inquiry(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) );
+ return true;
+}
- memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8);
- memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16);
+bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_ASSERT(p_msc->itf_num == itf_num);
- //------------- SCSI Read Capacity 10 -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
+ p_msc->configured = true;
- // NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence
- // Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work
- if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) )
+ //------------- Get Max Lun -------------//
+ TU_LOG2("MSC Get Max Lun\r\n");
+ tusb_control_request_t request =
{
- // clear stall TODO abstract clear stall function
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT },
- .bRequest = TUSB_REQ_CLEAR_FEATURE,
- .wValue = 0,
- .wIndex = p_msc->ep_in,
- .wLength = 0
- };
-
- TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL ));
-
- hcd_edpt_clear_stall(dev_addr, p_msc->ep_in);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = MSC_REQ_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = itf_num,
+ .wLength = 1
+ };
+ TU_ASSERT(tuh_control_xfer(dev_addr, &request, &p_msc->max_lun, config_get_maxlun_complete));
- //------------- SCSI Request Sense -------------//
- (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
+ return true;
+}
- //------------- Re-read SCSI Read Capactity -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
- }
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
- p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba );
- p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size );
+ msch_interface_t* p_msc = get_itf(dev_addr);
- p_msc->is_initialized = true;
+ // STALL means zero
+ p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0;
+ p_msc->max_lun++; // MAX LUN is minus 1 by specs
- tuh_msc_mounted_cb(dev_addr);
+ // TODO multiple LUN support
+ TU_LOG2("SCSI Test Unit Ready\r\n");
+ uint8_t const lun = 0;
+ tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete);
return true;
}
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
{
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
- if ( ep_addr == p_msc->ep_in )
+ if (csw->status == 0)
{
- if (p_msc->is_initialized)
- {
- tuh_msc_isr(dev_addr, event, xferred_bytes);
- }else
- { // still initializing under open subtask
- osal_semaphore_post(msch_sem_hdl, true);
- }
+ // Unit is ready, read its capacity
+ TU_LOG2("SCSI Read Capacity\r\n");
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete);
+ }else
+ {
+ // Note: During enumeration, some device fails Test Unit Ready and require a few retries
+ // with Request Sense to start working !!
+ // TODO limit number of retries
+ TU_LOG2("SCSI Request Sense\r\n");
+ TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete));
}
+
+ return true;
}
-void msch_close(uint8_t dev_addr)
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
{
- tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t));
- osal_semaphore_reset(msch_sem_hdl);
-
- tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback
+ TU_ASSERT(csw->status == 0);
+ TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete));
+ return true;
}
-//--------------------------------------------------------------------+
-// INTERNAL & HELPER
-//--------------------------------------------------------------------+
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
+{
+ TU_ASSERT(csw->status == 0);
+ msch_interface_t* p_msc = get_itf(dev_addr);
+
+ // Capacity response field: Block size and Last LBA are both Big-Endian
+ scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
+ p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
+ p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
+
+ // Mark enumeration is complete
+ p_msc->mounted = true;
+ if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
+
+ // notify usbh that driver enumeration is complete
+ usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
+
+ return true;
+}
#endif
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 76cf86853..7718ad4fe 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -27,180 +27,93 @@
#ifndef _TUSB_MSC_HOST_H_
#define _TUSB_MSC_HOST_H_
-#include "common/tusb_common.h"
-#include "host/usbh.h"
#include "msc.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** \addtogroup ClassDriver_MSC
- * @{
- * \defgroup MSC_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
//--------------------------------------------------------------------+
-// MASS STORAGE Application API
+// Class Driver Configuration
//--------------------------------------------------------------------+
-/** \brief Check if device supports MassStorage interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports
- * \retval false if device does not support or is not mounted
- */
-bool tuh_msc_is_mounted(uint8_t dev_addr);
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
- * can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted)
- * before calling this function
- */
-bool tuh_msc_is_busy(uint8_t dev_addr);
+#ifndef CFG_TUH_MSC_MAXLUN
+#define CFG_TUH_MSC_MAXLUN 4
+#endif
-/** \brief Get SCSI vendor's name of MassStorage device
- * \param[in] dev_addr device address
- * \return pointer to vendor's name or NULL if specified device does not support MassStorage
- * \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
- * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
- * command or allocate buffer for this.
- */
-uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr);
+typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
-/** \brief Get SCSI product's name of MassStorage device
- * \param[in] dev_addr device address
- * \return pointer to product's name or NULL if specified device does not support MassStorage
- * \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
- * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
- * command or allocate buffer for this.
- */
-uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr);
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
-/** \brief Get SCSI Capacity of MassStorage device
- * \param[in] dev_addr device address
- * \param[out] p_last_lba Last Logical Block Address of device
- * \param[out] p_block_size Block Size of device in bytes
- * \retval pointer to product's name or NULL if specified device does not support MassStorage
- * \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already
- * retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application
- * to re-send SCSI READ CAPACITY 10 command
- */
-tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size);
+// Check if device supports MassStorage interface.
+// This function true after tuh_msc_mounted_cb() and false after tuh_msc_unmounted_cb()
+bool tuh_msc_mounted(uint8_t dev_addr);
-/** \brief Perform SCSI READ 10 command to read data from MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] lba Starting Logical Block Address to be read
- * \param[in] block_count Number of Block to be read
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count);
+// Check if the interface is currently ready or busy transferring data
+bool tuh_msc_ready(uint8_t dev_addr);
-/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] lba Starting Logical Block Address to be written
- * \param[in] block_count Number of Block to be written
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count);
+// Get Max Lun
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
-/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data);
+// Get number of block
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
-/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor
+// Get block size in bytes
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
-//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in,
-// uint8_t const * p_command, uint8_t cmd_len,
-// uint8_t * p_response, uint32_t resp_len);
+// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
+// Complete callback is invoked when SCSI op is complete.
+// return true if success, false if there is already pending operation.
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb);
-//------------- Application Callback -------------//
-/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_msc_mounted_cb(uint8_t dev_addr);
+// Perform SCSI Inquiry command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb);
-/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_msc_unmounted_cb(uint8_t dev_addr);
+// Perform SCSI Test Unit Ready command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb);
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \param[in] xferred_bytes Number of bytes transferred via USB bus
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note
- */
-void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes);
+// Perform SCSI Request Sense 10 command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb);
+// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
+// Complete callback is invoked when SCSI op is complete.
+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);
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-typedef struct
-{
- uint8_t itf_numr;
- uint8_t ep_in;
- uint8_t ep_out;
+// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
+// Complete callback is invoked when SCSI op is complete.
+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);
- uint8_t max_lun;
- uint16_t block_size;
- uint32_t last_lba; // last logical block address
+// 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);
- volatile bool is_initialized;
- uint8_t vendor_id[8];
- uint8_t product_id[16];
+//------------- Application Callback -------------//
- msc_cbw_t cbw;
- msc_csw_t csw;
-}msch_interface_t;
+// Invoked when a device with MassStorage interface is mounted
+TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr);
-void msch_init(void);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void msch_close(uint8_t dev_addr);
+// Invoked when a device with MassStorage interface is unmounted
+TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+
+void msch_init (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 dev_addr, 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 /* _TUSB_MSC_HOST_H_ */
-
-/// @}
-/// @}
diff --git a/src/class/net/net_device.c b/src/class/net/net_device.c
new file mode 100644
index 000000000..9d9719ce4
--- /dev/null
+++ b/src/class/net/net_device.c
@@ -0,0 +1,441 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * 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 ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NET )
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "net_device.h"
+#include "rndis_protocol.h"
+
+void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+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;
+
+ bool ecm_mode;
+
+ // Endpoint descriptor use to open/close when receving SetInterface
+ // TODO since configuration descriptor may not be long-lived memory, we should
+ // keep a copy of endpoint attribute instead
+ uint8_t const * ecm_desc_epdata;
+
+} netd_interface_t;
+
+#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
+#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+
+struct ecm_notify_struct
+{
+ tusb_control_request_t header;
+ uint32_t downlink, uplink;
+};
+
+static const struct ecm_notify_struct ecm_notify_nc =
+{
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
+ .wValue = 1 /* Connected */,
+ .wLength = 0,
+ },
+};
+
+static const struct ecm_notify_struct ecm_notify_csc =
+{
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
+ .wLength = 8,
+ },
+ .downlink = 9728000,
+ .uplink = 9728000,
+};
+
+// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union
+{
+ uint8_t rndis_buf[120];
+ struct ecm_notify_struct ecm_buf;
+} notify;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// TODO remove CFG_TUSB_MEM_SECTION
+CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf;
+
+static bool can_xmit;
+
+void tud_network_recv_renew(void)
+{
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received));
+}
+
+static void do_in_xfer(uint8_t *buf, uint16_t len)
+{
+ can_xmit = false;
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len);
+}
+
+void netd_report(uint8_t *buf, uint16_t len)
+{
+ usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void netd_init(void)
+{
+ tu_memclr(&_netd_itf, sizeof(_netd_itf));
+}
+
+void netd_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ netd_init();
+}
+
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
+ TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
+ TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
+
+ bool const is_ecm = (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
+ 0x00 == itf_desc->bInterfaceProtocol);
+
+ TU_VERIFY(is_rndis || is_ecm, 0);
+
+ // confirm interface hasn't already been allocated
+ TU_ASSERT(0 == _netd_itf.ep_notif, 0);
+
+ // sanity check the descriptor
+ _netd_itf.ecm_mode = is_ecm;
+
+ //------------- Management Interface -------------//
+ _netd_itf.itf_num = itf_desc->bInterfaceNumber;
+
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+ // Communication Functional 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);
+ }
+
+ // notification endpoint (if any)
+ if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+ {
+ TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+
+ _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface -------------//
+ // - RNDIS Data followed immediately by a pair of endpoints
+ // - CDC-ECM data interface has 2 alternate settings
+ // - 0 : zero endpoints for inactive (default)
+ // - 1 : IN & OUT endpoints for active networking
+ TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+
+ do
+ {
+ tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+ TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
+ }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
+
+ // Pair of endpoints
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+
+ if ( _netd_itf.ecm_mode )
+ {
+ // ECM by default is in-active, save the endpoint attribute
+ // to open later when received setInterface
+ _netd_itf.ecm_desc_epdata = p_desc;
+ }else
+ {
+ // Open endpoint pair for RNDIS
+ TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
+
+ tud_network_init_cb();
+
+ // we are ready to transmit a packet
+ can_xmit = true;
+
+ // prepare for incoming packets
+ tud_network_recv_renew();
+ }
+
+ drv_len += 2*sizeof(tusb_desc_endpoint_t);
+
+ return drv_len;
+}
+
+static void ecm_report(bool nc)
+{
+ notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
+ notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
+ netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ switch ( request->bmRequestType_bit.type )
+ {
+ case TUSB_REQ_TYPE_STANDARD:
+ switch ( request->bRequest )
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ {
+ uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
+
+ tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
+ }
+ break;
+
+ case TUSB_REQ_SET_INTERFACE:
+ {
+ uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ uint8_t const req_alt = (uint8_t) request->wValue;
+
+ // Only valid for Data Interface with Alternate is either 0 or 1
+ TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
+
+ // ACM-ECM only: qequest to enable/disable network activities
+ TU_VERIFY(_netd_itf.ecm_mode);
+
+ _netd_itf.itf_data_alt = req_alt;
+
+ if ( _netd_itf.itf_data_alt )
+ {
+ // TODO since we don't actually close endpoint
+ // hack here to not re-open it
+ if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
+ {
+ TU_ASSERT(_netd_itf.ecm_desc_epdata);
+ TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+
+ // TODO should be merge with RNDIS's after endpoint opened
+ // Also should have opposite callback for application to disable network !!
+ tud_network_init_cb();
+ can_xmit = true; // we are ready to transmit a packet
+ tud_network_recv_renew(); // prepare for incoming packets
+ }
+ }else
+ {
+ // TODO close the endpoint pair
+ // For now pretend that we did, this should have no harm since host won't try to
+ // communicate with the endpoints again
+ // _netd_itf.ep_in = _netd_itf.ep_out = 0
+ }
+
+ tud_control_status(rhport, request);
+ }
+ break;
+
+ // unsupported request
+ default: return false;
+ }
+ break;
+
+ case TUSB_REQ_TYPE_CLASS:
+ TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+
+ if (_netd_itf.ecm_mode)
+ {
+ /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
+ if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
+ {
+ tud_control_xfer(rhport, request, NULL, 0);
+ ecm_report(true);
+ }
+ }
+ else
+ {
+ if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
+ uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
+ TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
+ tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
+ }
+ else
+ {
+ tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
+ }
+ }
+ break;
+
+ // unsupported request
+ default: return false;
+ }
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ // Handle RNDIS class control OUT only
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+ request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
+ _netd_itf.itf_num == request->wIndex)
+ {
+ if ( !_netd_itf.ecm_mode )
+ {
+ rndis_class_set_handler(notify.rndis_buf, request->wLength);
+ }
+ }
+ }
+
+ return true;
+}
+
+static void handle_incoming_packet(uint32_t len)
+{
+ uint8_t *pnt = received;
+ uint32_t size = 0;
+
+ if (_netd_itf.ecm_mode)
+ {
+ size = len;
+ }
+ else
+ {
+ rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
+ if (len >= sizeof(rndis_data_packet_t))
+ if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
+ if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
+ {
+ pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
+ size = r->DataLength;
+ }
+ }
+
+ if (!tud_network_recv_cb(pnt, size))
+ {
+ /* if a buffer was never handled by user code, we must renew on the user's behalf */
+ tud_network_recv_renew();
+ }
+}
+
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) rhport;
+ (void) result;
+
+ /* new packet received */
+ if ( ep_addr == _netd_itf.ep_out )
+ {
+ handle_incoming_packet(xferred_bytes);
+ }
+
+ /* 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)) )
+ {
+ do_in_xfer(NULL, 0); /* a ZLP is needed */
+ }
+ else
+ {
+ /* we're finally finished */
+ can_xmit = true;
+ }
+ }
+
+ if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
+ {
+ if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
+ }
+
+ return true;
+}
+
+bool tud_network_can_xmit(void)
+{
+ return can_xmit;
+}
+
+void tud_network_xmit(void *ref, uint16_t arg)
+{
+ uint8_t *data;
+ uint16_t len;
+
+ if (!can_xmit)
+ return;
+
+ len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
+ data = transmitted + len;
+
+ len += tud_network_xmit_cb(data, ref, arg);
+
+ if (!_netd_itf.ecm_mode)
+ {
+ rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
+ memset(hdr, 0, sizeof(rndis_data_packet_t));
+ hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
+ hdr->MessageLength = len;
+ hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset);
+ hdr->DataLength = len - sizeof(rndis_data_packet_t);
+ }
+
+ do_in_xfer(transmitted, len);
+}
+
+#endif
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
new file mode 100644
index 000000000..f030f3077
--- /dev/null
+++ b/src/class/net/net_device.h
@@ -0,0 +1,85 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * 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.
+ */
+
+#ifndef _TUSB_NET_DEVICE_H_
+#define _TUSB_NET_DEVICE_H_
+
+#include "class/cdc/cdc.h"
+
+/* 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 Tranmission Unit (in bytes) of the network, including Ethernet header */
+#ifndef CFG_TUD_NET_MTU
+#define CFG_TUD_NET_MTU 1514
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// client must provide this: initialize any network state back to the beginning
+void tud_network_init_cb(void);
+
+// client must provide this: return false if the packet buffer was not accepted
+bool tud_network_recv_cb(const uint8_t *src, uint16_t size);
+
+// client must provide this: copy from network stack packet pointer to dst
+uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg);
+
+// client must provide this: 48-bit MAC address
+// TODO removed later since it is not part of tinyusb stack
+extern const uint8_t tud_network_mac_address[6];
+
+// indicate to network driver that client has finished with the packet provided to network_recv_cb()
+void tud_network_recv_renew(void);
+
+// poll network driver for its ability to accept another packet to transmit
+bool tud_network_can_xmit(void);
+
+// if network_can_xmit() returns true, network_xmit() can be called once
+void tud_network_xmit(void *ref, uint16_t arg);
+
+//--------------------------------------------------------------------+
+// INTERNAL USBD-CLASS DRIVER API
+//--------------------------------------------------------------------+
+void netd_init (void);
+void netd_reset (uint8_t rhport);
+uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void netd_report (uint8_t *buf, uint16_t len);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_NET_DEVICE_H_ */
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index 916422752..c1ee49f45 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -77,16 +77,11 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_USBTMC)
-#include <string.h>
-#include "usbtmc.h"
-#include "usbtmc_device.h"
-#include "device/dcd.h"
#include "device/usbd.h"
-#include "osal/osal.h"
-
-// FIXME: I shouldn't need to include _pvt headers, but it is necessary for usbd_edpt_xfer, _stall, and _busy
#include "device/usbd_pvt.h"
+#include "usbtmc_device.h"
+
#ifdef xDEBUG
#include "uart_util.h"
static char logMsg[150];
@@ -132,9 +127,9 @@ typedef struct
uint8_t ep_int_in;
// IN buffer is only used for first packet, not the remainder
// in order to deal with prepending header
- uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_MAX_PACKET_SIZE];
// OUT buffer receives one packet at a time
- uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_MAX_PACKET_SIZE];
uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
@@ -146,7 +141,7 @@ typedef struct
usbtmc_capabilities_specific_t const * capabilities;
} usbtmc_interface_state_t;
-static usbtmc_interface_state_t usbtmc_state =
+CFG_TUSB_MEM_SECTION static usbtmc_interface_state_t usbtmc_state =
{
.itf_id = 0xFF,
};
@@ -260,35 +255,39 @@ void usbtmcd_init_cb(void)
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
}
-bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
+uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void)rhport;
- TU_ASSERT(usbtmc_state.state == STATE_CLOSED);
+
+ uint16_t drv_len;
uint8_t const * p_desc;
uint8_t found_endpoints = 0;
+ TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 0);
+ TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS, 0);
+
#ifndef NDEBUG
- TU_ASSERT(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS);
- TU_ASSERT(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS);
// Only 2 or 3 endpoints are allowed for USBTMC.
- TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3));
+ TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3), 0);
#endif
+ TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
+
// Interface
- (*p_length) = 0u;
+ drv_len = 0u;
p_desc = (uint8_t const *) itf_desc;
usbtmc_state.itf_id = itf_desc->bInterfaceNumber;
usbtmc_state.rhport = rhport;
- while (found_endpoints < itf_desc->bNumEndpoints)
+ while (found_endpoints < itf_desc->bNumEndpoints && drv_len <= max_len)
{
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
{
tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc;
switch(ep_desc->bmAttributes.xfer) {
case TUSB_XFER_BULK:
- TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE);
+ TU_ASSERT(ep_desc->wMaxPacketSize.size == USBTMCD_MAX_PACKET_SIZE, 0);
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
{
usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress;
@@ -299,45 +298,46 @@ bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
break;
case TUSB_XFER_INTERRUPT:
#ifndef NDEBUG
- TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN);
- TU_ASSERT(usbtmc_state.ep_int_in == 0);
+ TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN, 0);
+ TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
#endif
usbtmc_state.ep_int_in = ep_desc->bEndpointAddress;
break;
default:
- TU_ASSERT(false);
+ TU_ASSERT(false, 0);
}
- TU_VERIFY( dcd_edpt_open(rhport, ep_desc));
+ TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 0);
found_endpoints++;
}
- (*p_length) = (uint8_t)((*p_length) + p_desc[DESC_OFFSET_LEN]);
- p_desc = tu_desc_next(p_desc);
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
// bulk endpoints are required, but interrupt IN is optional
#ifndef NDEBUG
- TU_ASSERT(usbtmc_state.ep_bulk_in != 0);
- TU_ASSERT(usbtmc_state.ep_bulk_out != 0);
+ TU_ASSERT(usbtmc_state.ep_bulk_in != 0, 0);
+ TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
if (itf_desc->bNumEndpoints == 2)
{
- TU_ASSERT(usbtmc_state.ep_int_in == 0);
+ TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
}
else if (itf_desc->bNumEndpoints == 3)
{
- TU_ASSERT(usbtmc_state.ep_int_in != 0);
+ TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
}
#if (CFG_TUD_USBTMC_ENABLE_488)
if(usbtmc_state.capabilities->bmIntfcCapabilities488.is488_2 ||
usbtmc_state.capabilities->bmDevCapabilities488.SR1)
{
- TU_ASSERT(usbtmc_state.ep_int_in != 0);
+ TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
}
#endif
#endif
atomicChangeState(STATE_CLOSED, STATE_NAK);
tud_usbtmc_open_cb(itf_desc->iInterface);
- return true;
+ return drv_len;
}
// Tell USBTMC class to set its bulk-in EP to ACK so that it can
// receive USBTMC commands.
@@ -571,7 +571,13 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
return false;
}
-bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) {
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // nothing to do with DATA and ACK stage
+ if ( stage != CONTROL_STAGE_SETUP ) return true;
uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
#if (CFG_TUD_USBTMC_ENABLE_488)
@@ -590,7 +596,7 @@ bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * r
criticalEnter();
usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us
criticalLeave();
- tud_usmtmc_bulkOut_clearFeature_cb();
+ tud_usbtmc_bulkOut_clearFeature_cb();
}
else if (ep_addr == usbtmc_state.ep_bulk_in)
{
@@ -851,13 +857,4 @@ bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * r
TU_VERIFY(false);
}
-bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void)rhport;
- //------------- Class Specific Request -------------//
- TU_ASSERT (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
- return true;
-}
-
#endif /* CFG_TUD_TSMC */
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 4c52989c2..622800315 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -69,7 +69,7 @@ void tud_usbtmc_open_cb(uint8_t interface_id);
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader);
// transfer_complete does not imply that a message is complete.
bool tud_usbtmc_msg_data_cb( void *data, size_t len, bool transfer_complete);
-void tud_usmtmc_bulkOut_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
+void tud_usbtmc_bulkOut_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request);
bool tud_usbtmc_msgBulkIn_complete_cb(void);
@@ -108,12 +108,11 @@ bool tud_usbtmc_start_bus_read(void);
/* "callbacks" from USB device core */
-bool usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-void usbtmcd_reset_cb(uint8_t rhport);
-bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * request);
-bool usbtmcd_control_complete_cb(uint8_t rhport, tusb_control_request_t const * request);
-void usbtmcd_init_cb(void);
+uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+void usbtmcd_reset_cb(uint8_t rhport);
+bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+void usbtmcd_init_cb(void);
/************************************************************
* USBTMC Descriptor Templates
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 8db5005f4..6718a97bf 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -28,9 +28,11 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_VENDOR)
-#include "vendor_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "vendor_device.h"
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -72,9 +74,9 @@ uint32_t tud_vendor_n_available (uint8_t itf)
return tu_fifo_count(&_vendord_itf[itf].rx_ff);
}
-bool tud_vendor_n_peek(uint8_t itf, int pos, uint8_t* u8)
+bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8)
{
- return tu_fifo_peek_at(&_vendord_itf[itf].rx_ff, pos, u8);
+ return tu_fifo_peek(&_vendord_itf[itf].rx_ff, u8);
}
//--------------------------------------------------------------------+
@@ -146,8 +148,8 @@ void vendord_init(void)
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_itf->rx_ff, osal_mutex_create(&p_itf->rx_ff_mutex));
- tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex));
+ tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
#endif
}
}
@@ -166,8 +168,13 @@ void vendord_reset(uint8_t rhport)
}
}
-bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
+uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
+ TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass, 0);
+
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + itf_desc->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ TU_VERIFY(max_len >= drv_len, 0);
+
// Find available interface
vendord_interface_t* p_vendor = NULL;
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
@@ -178,18 +185,21 @@ bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16
break;
}
}
- TU_VERIFY(p_vendor);
+ TU_VERIFY(p_vendor, 0);
// Open endpoint pair with usbd helper
- TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in));
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
p_vendor->itf_num = itf_desc->bInterfaceNumber;
- (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
// Prepare for incoming data
- TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)));
+ if ( !usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)) )
+ {
+ TU_LOG_FAILED();
+ TU_BREAKPOINT();
+ }
- return true;
+ return drv_len;
}
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h
index 30fe94c3a..6d9c784c0 100644
--- a/src/class/vendor/vendor_device.h
+++ b/src/class/vendor/vendor_device.h
@@ -28,7 +28,6 @@
#define _TUSB_VENDOR_DEVICE_H_
#include "common/tusb_common.h"
-#include "device/usbd.h"
#ifndef CFG_TUD_VENDOR_EPSIZE
#define CFG_TUD_VENDOR_EPSIZE 64
@@ -45,7 +44,7 @@ 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, int pos, uint8_t* u8);
+bool tud_vendor_n_peek (uint8_t itf, uint8_t* u8);
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
uint32_t tud_vendor_n_write_available (uint8_t itf);
@@ -59,7 +58,7 @@ uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
static inline bool tud_vendor_mounted (void);
static inline uint32_t tud_vendor_available (void);
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
-static inline bool tud_vendor_peek (int pos, uint8_t* u8);
+static inline bool tud_vendor_peek (uint8_t* u8);
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
static inline uint32_t tud_vendor_write_str (char const* str);
static inline uint32_t tud_vendor_write_available (void);
@@ -95,9 +94,9 @@ static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
return tud_vendor_n_read(0, buffer, bufsize);
}
-static inline bool tud_vendor_peek (int pos, uint8_t* u8)
+static inline bool tud_vendor_peek (uint8_t* u8)
{
- return tud_vendor_n_peek(0, pos, u8);
+ return tud_vendor_n_peek(0, u8);
}
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
@@ -118,10 +117,10 @@ static inline uint32_t tud_vendor_write_available (void)
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void vendord_init(void);
-void vendord_reset(uint8_t rhport);
-bool vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void vendord_init(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);
+bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
diff --git a/src/class/vendor/vendor_host.c b/src/class/vendor/vendor_host.c
index 3e8dac0f6..f7a6b2bf0 100644
--- a/src/class/vendor/vendor_host.c
+++ b/src/class/vendor/vendor_host.c
@@ -31,7 +31,7 @@
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
+#include "host/usbh.h"
#include "vendor_host.h"
//--------------------------------------------------------------------+
@@ -66,7 +66,7 @@ tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t pr
return TUSB_ERROR_INTERFACE_IS_BUSY;
}
- (void) hcd_pipe_xfer( custom_interface[dev_addr-1].pipe_in, p_buffer, length, true);
+ (void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_in, p_buffer, length);
return TUSB_ERROR_NONE;
}
@@ -80,7 +80,7 @@ tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t p
return TUSB_ERROR_INTERFACE_IS_BUSY;
}
- (void) hcd_pipe_xfer( custom_interface[dev_addr-1].pipe_out, p_data, length, true);
+ (void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_out, p_data, length);
return TUSB_ERROR_NONE;
}
@@ -107,7 +107,7 @@ tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
&custom_interface[dev_addr-1].pipe_in : &custom_interface[dev_addr-1].pipe_out;
- *p_pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
+ *p_pipe_hdl = usbh_edpt_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
TU_ASSERT ( pipehandle_is_valid(*p_pipe_hdl), TUSB_ERROR_HCD_OPEN_PIPE_FAILED );
p_desc = tu_desc_next(p_desc);
diff --git a/src/class/vendor/vendor_host.h b/src/class/vendor/vendor_host.h
index fa1879376..07fa56c61 100644
--- a/src/class/vendor/vendor_host.h
+++ b/src/class/vendor/vendor_host.h
@@ -24,15 +24,10 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup group_class
- * \defgroup Group_Custom Custom Class (not supported yet)
- * @{ */
-
#ifndef _TUSB_VENDOR_HOST_H_
#define _TUSB_VENDOR_HOST_H_
#include "common/tusb_common.h"
-#include "host/usbh.h"
#ifdef __cplusplus
extern "C" {
@@ -70,5 +65,3 @@ void cush_close(uint8_t dev_addr);
#endif
#endif /* _TUSB_VENDOR_HOST_H_ */
-
-/** @} */