Initial project setup
This commit is contained in:
41
managed_components/espressif__tinyusb/src/CMakeLists.txt
Normal file
41
managed_components/espressif__tinyusb/src/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
# Add tinyusb to a existing target, DCD and HCD drivers are not included
|
||||
function(tinyusb_target_add TARGET)
|
||||
target_sources(${TARGET} PRIVATE
|
||||
# common
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tusb.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c
|
||||
# device
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_rt_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/mtp/mtp_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ncm_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c
|
||||
# host
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/usbh.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/hub.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c
|
||||
# typec
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/typec/usbc.c
|
||||
)
|
||||
target_include_directories(${TARGET} PUBLIC
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}
|
||||
# TODO for net driver, should be removed/changed
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
|
||||
)
|
||||
endfunction()
|
||||
961
managed_components/espressif__tinyusb/src/class/audio/audio.h
Normal file
961
managed_components/espressif__tinyusb/src/class/audio/audio.h
Normal file
@@ -0,0 +1,961 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2020 Reinhard Panhuber
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup group_class
|
||||
* \defgroup ClassDriver_Audio Audio
|
||||
* Currently only MIDI subclass is supported
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_AUDIO_H__
|
||||
#define _TUSB_AUDIO_H__
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Audio Device Class Codes
|
||||
|
||||
/// A.2 - Audio Function Subclass Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
|
||||
} audio_function_subclass_type_t;
|
||||
|
||||
/// A.3 - Audio Function Protocol Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
|
||||
AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
|
||||
} audio_function_protocol_code_t;
|
||||
|
||||
/// A.5 - Audio Interface Subclass Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SUBCLASS_UNDEFINED = 0x00,
|
||||
AUDIO_SUBCLASS_CONTROL , ///< Audio Control
|
||||
AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
|
||||
AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
|
||||
} audio_subclass_type_t;
|
||||
|
||||
/// A.6 - Audio Interface Protocol Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
|
||||
AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
|
||||
} audio_interface_protocol_code_t;
|
||||
|
||||
/// A.7 - Audio Function Category Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FUNC_UNDEF = 0x00,
|
||||
AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
|
||||
AUDIO_FUNC_HOME_THEATER = 0x02,
|
||||
AUDIO_FUNC_MICROPHONE = 0x03,
|
||||
AUDIO_FUNC_HEADSET = 0x04,
|
||||
AUDIO_FUNC_TELEPHONE = 0x05,
|
||||
AUDIO_FUNC_CONVERTER = 0x06,
|
||||
AUDIO_FUNC_SOUND_RECODER = 0x07,
|
||||
AUDIO_FUNC_IO_BOX = 0x08,
|
||||
AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
|
||||
AUDIO_FUNC_PRO_AUDIO = 0x0A,
|
||||
AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
|
||||
AUDIO_FUNC_CONTROL_PANEL = 0x0C,
|
||||
AUDIO_FUNC_OTHER = 0xFF,
|
||||
} audio_function_code_t;
|
||||
|
||||
/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
|
||||
AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
|
||||
AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
|
||||
AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
|
||||
AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
|
||||
AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
|
||||
AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
|
||||
AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
|
||||
AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
|
||||
AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
|
||||
AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
|
||||
AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
|
||||
AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
|
||||
AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
|
||||
} audio_cs_ac_interface_subtype_t;
|
||||
|
||||
/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
|
||||
AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
|
||||
AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
|
||||
AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
|
||||
AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
|
||||
} audio_cs_as_interface_subtype_t;
|
||||
|
||||
/// A.11 - Effect Unit Effect Types
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_EFFECT_TYPE_UNDEF = 0x00,
|
||||
AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
|
||||
AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
|
||||
AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
|
||||
AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
|
||||
} audio_effect_unit_effect_type_t;
|
||||
|
||||
/// A.12 - Processing Unit Process Types
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_PROCESS_TYPE_UNDEF = 0x00,
|
||||
AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
|
||||
AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
|
||||
AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
|
||||
} audio_processing_unit_process_type_t;
|
||||
|
||||
/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
|
||||
AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
|
||||
} audio_cs_ep_subtype_t;
|
||||
|
||||
/// A.14 - Audio Class-Specific Request Codes
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CS_REQ_UNDEF = 0x00,
|
||||
AUDIO_CS_REQ_CUR = 0x01,
|
||||
AUDIO_CS_REQ_RANGE = 0x02,
|
||||
AUDIO_CS_REQ_MEM = 0x03,
|
||||
} audio_cs_req_t;
|
||||
|
||||
/// A.17 - Control Selector Codes
|
||||
|
||||
/// A.17.1 - Clock Source Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CS_CTRL_UNDEF = 0x00,
|
||||
AUDIO_CS_CTRL_SAM_FREQ = 0x01,
|
||||
AUDIO_CS_CTRL_CLK_VALID = 0x02,
|
||||
} audio_clock_src_control_selector_t;
|
||||
|
||||
/// A.17.2 - Clock Selector Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CX_CTRL_UNDEF = 0x00,
|
||||
AUDIO_CX_CTRL_CONTROL = 0x01,
|
||||
} audio_clock_sel_control_selector_t;
|
||||
|
||||
/// A.17.3 - Clock Multiplier Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CM_CTRL_UNDEF = 0x00,
|
||||
AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
|
||||
AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
|
||||
} audio_clock_mul_control_selector_t;
|
||||
|
||||
/// A.17.4 - Terminal Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_TE_CTRL_UNDEF = 0x00,
|
||||
AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
|
||||
AUDIO_TE_CTRL_CONNECTOR = 0x02,
|
||||
AUDIO_TE_CTRL_OVERLOAD = 0x03,
|
||||
AUDIO_TE_CTRL_CLUSTER = 0x04,
|
||||
AUDIO_TE_CTRL_UNDERFLOW = 0x05,
|
||||
AUDIO_TE_CTRL_OVERFLOW = 0x06,
|
||||
AUDIO_TE_CTRL_LATENCY = 0x07,
|
||||
} audio_terminal_control_selector_t;
|
||||
|
||||
/// A.17.5 - Mixer Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_MU_CTRL_UNDEF = 0x00,
|
||||
AUDIO_MU_CTRL_MIXER = 0x01,
|
||||
AUDIO_MU_CTRL_CLUSTER = 0x02,
|
||||
AUDIO_MU_CTRL_UNDERFLOW = 0x03,
|
||||
AUDIO_MU_CTRL_OVERFLOW = 0x04,
|
||||
AUDIO_MU_CTRL_LATENCY = 0x05,
|
||||
} audio_mixer_control_selector_t;
|
||||
|
||||
/// A.17.6 - Selector Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SU_CTRL_UNDEF = 0x00,
|
||||
AUDIO_SU_CTRL_SELECTOR = 0x01,
|
||||
AUDIO_SU_CTRL_LATENCY = 0x02,
|
||||
} audio_sel_control_selector_t;
|
||||
|
||||
/// A.17.7 - Feature Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FU_CTRL_UNDEF = 0x00,
|
||||
AUDIO_FU_CTRL_MUTE = 0x01,
|
||||
AUDIO_FU_CTRL_VOLUME = 0x02,
|
||||
AUDIO_FU_CTRL_BASS = 0x03,
|
||||
AUDIO_FU_CTRL_MID = 0x04,
|
||||
AUDIO_FU_CTRL_TREBLE = 0x05,
|
||||
AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
|
||||
AUDIO_FU_CTRL_AGC = 0x07,
|
||||
AUDIO_FU_CTRL_DELAY = 0x08,
|
||||
AUDIO_FU_CTRL_BASS_BOOST = 0x09,
|
||||
AUDIO_FU_CTRL_LOUDNESS = 0x0A,
|
||||
AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
|
||||
AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
|
||||
AUDIO_FU_CTRL_INVERTER = 0x0D,
|
||||
AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
|
||||
AUDIO_FU_CTRL_OVERVLOW = 0x0F,
|
||||
AUDIO_FU_CTRL_LATENCY = 0x10,
|
||||
} audio_feature_unit_control_selector_t;
|
||||
|
||||
/// A.17.8 Effect Unit Control Selectors
|
||||
|
||||
/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_PE_CTRL_UNDEF = 0x00,
|
||||
AUDIO_PE_CTRL_ENABLE = 0x01,
|
||||
AUDIO_PE_CTRL_CENTERFREQ = 0x02,
|
||||
AUDIO_PE_CTRL_QFACTOR = 0x03,
|
||||
AUDIO_PE_CTRL_GAIN = 0x04,
|
||||
AUDIO_PE_CTRL_UNDERFLOW = 0x05,
|
||||
AUDIO_PE_CTRL_OVERFLOW = 0x06,
|
||||
AUDIO_PE_CTRL_LATENCY = 0x07,
|
||||
} audio_parametric_equalizer_control_selector_t;
|
||||
|
||||
/// A.17.8.2 Reverberation Effect Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_RV_CTRL_UNDEF = 0x00,
|
||||
AUDIO_RV_CTRL_ENABLE = 0x01,
|
||||
AUDIO_RV_CTRL_TYPE = 0x02,
|
||||
AUDIO_RV_CTRL_LEVEL = 0x03,
|
||||
AUDIO_RV_CTRL_TIME = 0x04,
|
||||
AUDIO_RV_CTRL_FEEDBACK = 0x05,
|
||||
AUDIO_RV_CTRL_PREDELAY = 0x06,
|
||||
AUDIO_RV_CTRL_DENSITY = 0x07,
|
||||
AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
|
||||
AUDIO_RV_CTRL_UNDERFLOW = 0x09,
|
||||
AUDIO_RV_CTRL_OVERFLOW = 0x0A,
|
||||
AUDIO_RV_CTRL_LATENCY = 0x0B,
|
||||
} audio_reverberation_effect_control_selector_t;
|
||||
|
||||
/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_MD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_MD_CTRL_ENABLE = 0x01,
|
||||
AUDIO_MD_CTRL_BALANCE = 0x02,
|
||||
AUDIO_MD_CTRL_RATE = 0x03,
|
||||
AUDIO_MD_CTRL_DEPTH = 0x04,
|
||||
AUDIO_MD_CTRL_TIME = 0x05,
|
||||
AUDIO_MD_CTRL_FEEDBACK = 0x06,
|
||||
AUDIO_MD_CTRL_UNDERFLOW = 0x07,
|
||||
AUDIO_MD_CTRL_OVERFLOW = 0x08,
|
||||
AUDIO_MD_CTRL_LATENCY = 0x09,
|
||||
} audio_modulation_delay_control_selector_t;
|
||||
|
||||
/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_DR_CTRL_UNDEF = 0x00,
|
||||
AUDIO_DR_CTRL_ENABLE = 0x01,
|
||||
AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
|
||||
AUDIO_DR_CTRL_MAXAMPL = 0x03,
|
||||
AUDIO_DR_CTRL_THRESHOLD = 0x04,
|
||||
AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
|
||||
AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
|
||||
AUDIO_DR_CTRL_UNDERFLOW = 0x07,
|
||||
AUDIO_DR_CTRL_OVERFLOW = 0x08,
|
||||
AUDIO_DR_CTRL_LATENCY = 0x09,
|
||||
} audio_dynamic_range_compression_control_selector_t;
|
||||
|
||||
/// A.17.9 Processing Unit Control Selectors
|
||||
|
||||
/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_UD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_UD_CTRL_ENABLE = 0x01,
|
||||
AUDIO_UD_CTRL_MODE_SELECT = 0x02,
|
||||
AUDIO_UD_CTRL_CLUSTER = 0x03,
|
||||
AUDIO_UD_CTRL_UNDERFLOW = 0x04,
|
||||
AUDIO_UD_CTRL_OVERFLOW = 0x05,
|
||||
AUDIO_UD_CTRL_LATENCY = 0x06,
|
||||
} audio_up_down_mix_control_selector_t;
|
||||
|
||||
/// A.17.9.2 Dolby Prologic ™ Processing Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_DP_CTRL_UNDEF = 0x00,
|
||||
AUDIO_DP_CTRL_ENABLE = 0x01,
|
||||
AUDIO_DP_CTRL_MODE_SELECT = 0x02,
|
||||
AUDIO_DP_CTRL_CLUSTER = 0x03,
|
||||
AUDIO_DP_CTRL_UNDERFLOW = 0x04,
|
||||
AUDIO_DP_CTRL_OVERFLOW = 0x05,
|
||||
AUDIO_DP_CTRL_LATENCY = 0x06,
|
||||
} audio_dolby_prologic_control_selector_t;
|
||||
|
||||
/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
|
||||
AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
|
||||
AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
|
||||
AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
|
||||
AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
|
||||
AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
|
||||
} audio_stereo_extender_control_selector_t;
|
||||
|
||||
/// A.17.10 Extension Unit Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_XU_CTRL_UNDEF = 0x00,
|
||||
AUDIO_XU_CTRL_ENABLE = 0x01,
|
||||
AUDIO_XU_CTRL_CLUSTER = 0x02,
|
||||
AUDIO_XU_CTRL_UNDERFLOW = 0x03,
|
||||
AUDIO_XU_CTRL_OVERFLOW = 0x04,
|
||||
AUDIO_XU_CTRL_LATENCY = 0x05,
|
||||
} audio_extension_unit_control_selector_t;
|
||||
|
||||
/// A.17.11 AudioStreaming Interface Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_AS_CTRL_UNDEF = 0x00,
|
||||
AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
|
||||
AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
|
||||
AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
|
||||
} audio_audiostreaming_interface_control_selector_t;
|
||||
|
||||
/// A.17.12 Encoder Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_EN_CTRL_UNDEF = 0x00,
|
||||
AUDIO_EN_CTRL_BIT_RATE = 0x01,
|
||||
AUDIO_EN_CTRL_QUALITY = 0x02,
|
||||
AUDIO_EN_CTRL_VBR = 0x03,
|
||||
AUDIO_EN_CTRL_TYPE = 0x04,
|
||||
AUDIO_EN_CTRL_UNDERFLOW = 0x05,
|
||||
AUDIO_EN_CTRL_OVERFLOW = 0x06,
|
||||
AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
|
||||
AUDIO_EN_CTRL_PARAM1 = 0x08,
|
||||
AUDIO_EN_CTRL_PARAM2 = 0x09,
|
||||
AUDIO_EN_CTRL_PARAM3 = 0x0A,
|
||||
AUDIO_EN_CTRL_PARAM4 = 0x0B,
|
||||
AUDIO_EN_CTRL_PARAM5 = 0x0C,
|
||||
AUDIO_EN_CTRL_PARAM6 = 0x0D,
|
||||
AUDIO_EN_CTRL_PARAM7 = 0x0E,
|
||||
AUDIO_EN_CTRL_PARAM8 = 0x0F,
|
||||
} audio_encoder_control_selector_t;
|
||||
|
||||
/// A.17.13 Decoder Control Selectors
|
||||
|
||||
/// A.17.13.1 MPEG Decoder Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_MPD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
|
||||
AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
|
||||
AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
|
||||
AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
|
||||
AUDIO_MPD_CTRL_SCALING = 0x05,
|
||||
AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
|
||||
AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
|
||||
AUDIO_MPD_CTRL_OVERFLOW = 0x08,
|
||||
AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
|
||||
} audio_MPEG_decoder_control_selector_t;
|
||||
|
||||
/// A.17.13.2 AC-3 Decoder Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_AD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_AD_CTRL_MODE = 0x01,
|
||||
AUDIO_AD_CTRL_DYN_RANGE = 0x02,
|
||||
AUDIO_AD_CTRL_SCALING = 0x03,
|
||||
AUDIO_AD_CTRL_HILO_SCALING = 0x04,
|
||||
AUDIO_AD_CTRL_UNDERFLOW = 0x05,
|
||||
AUDIO_AD_CTRL_OVERFLOW = 0x06,
|
||||
AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
|
||||
} audio_AC3_decoder_control_selector_t;
|
||||
|
||||
/// A.17.13.3 WMA Decoder Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_WD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_WD_CTRL_UNDERFLOW = 0x01,
|
||||
AUDIO_WD_CTRL_OVERFLOW = 0x02,
|
||||
AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
|
||||
} audio_WMA_decoder_control_selector_t;
|
||||
|
||||
/// A.17.13.4 DTS Decoder Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_DD_CTRL_UNDEF = 0x00,
|
||||
AUDIO_DD_CTRL_UNDERFLOW = 0x01,
|
||||
AUDIO_DD_CTRL_OVERFLOW = 0x02,
|
||||
AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
|
||||
} audio_DTS_decoder_control_selector_t;
|
||||
|
||||
/// A.17.14 Endpoint Control Selectors
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_EP_CTRL_UNDEF = 0x00,
|
||||
AUDIO_EP_CTRL_PITCH = 0x01,
|
||||
AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
|
||||
AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
|
||||
} audio_EP_control_selector_t;
|
||||
|
||||
/// Terminal Types
|
||||
|
||||
/// 2.1 - Audio Class-Terminal Types UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
|
||||
AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
|
||||
AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
|
||||
} audio_terminal_type_t;
|
||||
|
||||
/// 2.2 - Audio Class-Input Terminal Types UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
|
||||
AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
|
||||
AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
|
||||
AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
|
||||
AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
|
||||
AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
|
||||
AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
|
||||
} audio_terminal_input_type_t;
|
||||
|
||||
/// 2.3 - Audio Class-Output Terminal Types UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
|
||||
AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
|
||||
AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
|
||||
AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
|
||||
AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
|
||||
AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
|
||||
AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
|
||||
AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
|
||||
} audio_terminal_output_type_t;
|
||||
|
||||
/// Rest is yet to be implemented
|
||||
|
||||
/// Additional Audio Device Class Codes - Source: Audio Data Formats
|
||||
|
||||
/// A.1 - Audio Class-Format Type Codes UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
|
||||
AUDIO_FORMAT_TYPE_I = 0x01,
|
||||
AUDIO_FORMAT_TYPE_II = 0x02,
|
||||
AUDIO_FORMAT_TYPE_III = 0x03,
|
||||
AUDIO_FORMAT_TYPE_IV = 0x04,
|
||||
AUDIO_EXT_FORMAT_TYPE_I = 0x81,
|
||||
AUDIO_EXT_FORMAT_TYPE_II = 0x82,
|
||||
AUDIO_EXT_FORMAT_TYPE_III = 0x83,
|
||||
} audio_format_type_t;
|
||||
|
||||
// A.2.1 - Audio Class-Audio Data Format Type I UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
|
||||
AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
|
||||
AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
|
||||
AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
|
||||
AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
|
||||
AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
|
||||
} audio_data_format_type_I_t;
|
||||
|
||||
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
|
||||
|
||||
/// Audio Class-Control Values UAC2
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_CTRL_NONE = 0x00, ///< No Host access
|
||||
AUDIO_CTRL_R = 0x01, ///< Host read access only
|
||||
AUDIO_CTRL_RW = 0x03, ///< Host read write access
|
||||
} audio_control_t;
|
||||
|
||||
/// 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 = 0x80000000u,
|
||||
} 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;
|
||||
TU_VERIFY_STATIC(sizeof(audio_desc_cs_ac_interface_t) == 9, "size is not correct");
|
||||
|
||||
/// AUDIO Clock Source Descriptor (4.7.2.1)
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
|
||||
uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
|
||||
uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
|
||||
uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
|
||||
uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
|
||||
uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
|
||||
} audio_desc_clock_source_t;
|
||||
|
||||
/// AUDIO Clock 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.
|
||||
uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this terminal.
|
||||
uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
|
||||
uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
|
||||
uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
|
||||
uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
|
||||
uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
|
||||
uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first logical channel.
|
||||
uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
|
||||
uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
|
||||
} audio_desc_input_terminal_t;
|
||||
|
||||
/// 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]; \
|
||||
}
|
||||
|
||||
// 6.1 Interrupt Data Message Format
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bInfo;
|
||||
uint8_t bAttribute;
|
||||
union
|
||||
{
|
||||
uint16_t wValue;
|
||||
struct
|
||||
{
|
||||
uint8_t wValue_cn_or_mcn;
|
||||
uint8_t wValue_cs;
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
uint16_t wIndex;
|
||||
struct
|
||||
{
|
||||
uint8_t wIndex_ep_or_int;
|
||||
uint8_t wIndex_entity_id;
|
||||
};
|
||||
};
|
||||
} audio_interrupt_data_t;
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
1834
managed_components/espressif__tinyusb/src/class/audio/audio_device.c
Normal file
1834
managed_components/espressif__tinyusb/src/class/audio/audio_device.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2020 Reinhard Panhuber
|
||||
* Copyright (c) 2023 HiFiPhile
|
||||
*
|
||||
* 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
|
||||
|
||||
// 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
|
||||
|
||||
// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
|
||||
#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
|
||||
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
||||
// Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set().
|
||||
// Can be override by tud_audio_feedback_format_correction_cb()
|
||||
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
|
||||
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
|
||||
#endif
|
||||
|
||||
// Enable/disable interrupt EP (required for notifying host of control changes)
|
||||
#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
|
||||
#endif
|
||||
|
||||
// Audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
|
||||
#define CFG_TUD_AUDIO_INTERRUPT_EP_SZ 6
|
||||
|
||||
#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
|
||||
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);
|
||||
tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN
|
||||
uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
|
||||
bool tud_audio_n_clear_ep_in_ff (uint8_t func_id);
|
||||
tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
bool tud_audio_int_n_write (uint8_t func_id, const audio_interrupt_data_t * data);
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Interface0)
|
||||
//--------------------------------------------------------------------+
|
||||
static inline bool tud_audio_mounted (void);
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
static inline uint16_t tud_audio_available (void);
|
||||
static inline bool tud_audio_clear_ep_out_ff (void);
|
||||
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_IN
|
||||
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
|
||||
|
||||
// INT CTR API
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
static inline bool tud_audio_int_write (const audio_interrupt_data_t * data);
|
||||
#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
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_IN
|
||||
// Invoked in ISR context once an audio packet was sent successfully.
|
||||
// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_write() in I2S receive callback.
|
||||
bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
// Invoked in ISR context once an audio packet was received successfully.
|
||||
// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_read() in I2S transmit callback.
|
||||
bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
|
||||
// Note about feedback calculation
|
||||
//
|
||||
// Option 1 - AUDIO_FEEDBACK_METHOD_FIFO_COUNT
|
||||
// Feedback value is calculated within the audio driver by regulating the FIFO level to half fill.
|
||||
// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, well tested
|
||||
// (Windows, Linux, OSX) with a reliable result so far.
|
||||
// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is introduced.
|
||||
//
|
||||
// Option 2 - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED / AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
|
||||
// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from
|
||||
// which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
|
||||
// See tud_audio_set_fb_params() and tud_audio_feedback_update()
|
||||
// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a smaller delay is possible.
|
||||
// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point is the reading of the cycle counter value of f_m.
|
||||
// It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
|
||||
// Long-term drift could occur since error is accumulated.
|
||||
//
|
||||
// Option 3 - manual
|
||||
// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer.
|
||||
// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load.
|
||||
// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay is introduced.
|
||||
|
||||
|
||||
// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed.
|
||||
//
|
||||
// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default,
|
||||
// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set or tud_audio_feedback_format_correction_cb()
|
||||
// return true, then tinyusb expects 16.16 format and handles the conversion to 10.14 on FS.
|
||||
//
|
||||
// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and it seems the
|
||||
// driver can work with either format.
|
||||
//
|
||||
// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value.
|
||||
//
|
||||
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
|
||||
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
|
||||
// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13
|
||||
// for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
|
||||
// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K)
|
||||
// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
|
||||
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
|
||||
|
||||
// Update feedback value with passed MCLK cycles since last time this update function is called.
|
||||
// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented
|
||||
// This function will also call tud_audio_feedback_set()
|
||||
// return feedback value in 16.16 for reference (0 for error)
|
||||
// Example :
|
||||
// binterval=3 (4ms); FS = 48kHz; MCLK = 12.288MHz
|
||||
// In 4 SOF MCLK counted 49152 cycles
|
||||
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles);
|
||||
|
||||
enum {
|
||||
AUDIO_FEEDBACK_METHOD_DISABLED,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
|
||||
AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only
|
||||
AUDIO_FEEDBACK_METHOD_FIFO_COUNT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t method;
|
||||
uint32_t sample_freq; // sample frequency in Hz
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
|
||||
}frequency;
|
||||
|
||||
};
|
||||
}audio_feedback_params_t;
|
||||
|
||||
// Invoked when needed to set feedback parameters
|
||||
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
|
||||
|
||||
// Callback in ISR context, invoked periodically according to feedback endpoint bInterval.
|
||||
// Could be used to compute and update feedback value, should be placed in RAM if possible
|
||||
// frame_number : current SOF count
|
||||
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
|
||||
TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
|
||||
|
||||
// (Full-Speed only) Callback to set feedback format correction is applied or not,
|
||||
// default to CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if not implemented.
|
||||
bool tud_audio_feedback_format_correction_cb(uint8_t func_id);
|
||||
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
void tud_audio_int_done_cb(uint8_t rhport);
|
||||
#endif
|
||||
|
||||
// Invoked when audio set interface request received
|
||||
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
|
||||
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
|
||||
|
||||
// backward compatible for typo
|
||||
#define tud_audio_set_itf_close_EP_cb tud_audio_set_itf_close_ep_cb
|
||||
|
||||
// Invoked when audio class specific set request received for an EP
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_mounted(void) {
|
||||
return tud_audio_n_mounted(0);
|
||||
}
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_available(void) {
|
||||
return tud_audio_n_available(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize) {
|
||||
return tud_audio_n_read(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_out_ff(void) {
|
||||
return tud_audio_n_clear_ep_out_ff(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE 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_IN
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_write(const void * data, uint16_t len) {
|
||||
return tud_audio_n_write(0, data, len);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_in_ff(void) {
|
||||
return tud_audio_n_clear_ep_in_ff(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tu_fifo_t* tud_audio_get_ep_in_ff(void) {
|
||||
return tud_audio_n_get_ep_in_ff(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_int_write(const audio_interrupt_data_t * data) {
|
||||
return tud_audio_int_n_write(0, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
|
||||
TU_ATTR_ALWAYS_INLINE 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);
|
||||
bool audiod_deinit (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);
|
||||
bool audiod_xfer_isr (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void audiod_sof_isr (uint8_t rhport, uint32_t frame_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_AUDIO_DEVICE_H_ */
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
298
managed_components/espressif__tinyusb/src/class/bth/bth_device.c
Normal file
298
managed_components/espressif__tinyusb/src/class/bth/bth_device.c
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* 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 (CFG_TUD_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;
|
||||
uint16_t ep_acl_in_pkt_sz;
|
||||
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];
|
||||
|
||||
// Previous amount of bytes sent when issuing ZLP
|
||||
uint32_t prev_xferred_bytes;
|
||||
} btd_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(epout_buf, CFG_TUD_BTH_DATA_EPSIZE);
|
||||
TUD_EPBUF_TYPE_DEF(bt_hci_cmd_t, hci_cmd);
|
||||
} btd_epbuf_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static btd_interface_t _btd_itf;
|
||||
CFG_TUD_MEM_SECTION static btd_epbuf_t _btd_epbuf;
|
||||
|
||||
static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) {
|
||||
uint8_t const rhport = 0;
|
||||
|
||||
// skip if previous transfer not complete
|
||||
TU_VERIFY(!usbd_edpt_busy(rhport, ep));
|
||||
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, ep, data, len));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len) {
|
||||
(void) hci_cmd;
|
||||
(void) cmd_len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len) {
|
||||
(void) acl_data;
|
||||
(void) data_len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes) {
|
||||
(void) sent_bytes;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes) {
|
||||
(void) sent_bytes;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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));
|
||||
}
|
||||
|
||||
bool btd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
TU_ASSERT(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;
|
||||
|
||||
desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep);
|
||||
|
||||
// Open endpoint pair
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, (uint8_t const *) desc_ep, 2,
|
||||
TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
|
||||
&_btd_itf.ep_acl_in),
|
||||
0);
|
||||
|
||||
// Save acl in endpoint max packet size
|
||||
tusb_desc_endpoint_t const *desc_ep_acl_in = desc_ep;
|
||||
for (size_t p = 0; p < 2; p++) {
|
||||
if (tu_edpt_dir(desc_ep_acl_in->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
_btd_itf.ep_acl_in_pkt_sz = tu_edpt_packet_size(desc_ep_acl_in);
|
||||
break;
|
||||
}
|
||||
desc_ep_acl_in = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep_acl_in);
|
||||
}
|
||||
|
||||
itf_desc = (tusb_desc_interface_t const *) tu_desc_next(tu_desc_next(desc_ep));
|
||||
|
||||
// Prepare for incoming data from host
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
|
||||
|
||||
drv_len = hci_itf_size;
|
||||
|
||||
// Ensure this is still BT Primary Controller
|
||||
TU_ASSERT(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
|
||||
TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol,
|
||||
0);
|
||||
TU_ASSERT(itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size + drv_len);
|
||||
|
||||
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) tu_edpt_packet_size(desc_ep);
|
||||
|
||||
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) tu_edpt_packet_size(desc_ep);
|
||||
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) tu_edpt_packet_size(desc_ep);
|
||||
|
||||
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) tu_edpt_packet_size(desc_ep);
|
||||
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
|
||||
// also compatible with historical mode if enabled
|
||||
TU_VERIFY((request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0) ||
|
||||
(CFG_TUD_BTH_HISTORICAL_COMPATIBLE && request->bRequest == 0xe0));
|
||||
} 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_epbuf.hci_cmd, sizeof(bt_hci_cmd_t));
|
||||
} else if (stage == CONTROL_STAGE_DATA) {
|
||||
// Handle class request only
|
||||
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||
|
||||
tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
|
||||
uint32_t xferred_bytes) {
|
||||
// received new data from host
|
||||
if (ep_addr == _btd_itf.ep_acl_out) {
|
||||
tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes);
|
||||
|
||||
// prepare for next data
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
|
||||
} else if (ep_addr == _btd_itf.ep_ev) {
|
||||
tud_bt_event_sent_cb((uint16_t) xferred_bytes);
|
||||
} else if (ep_addr == _btd_itf.ep_acl_in) {
|
||||
if ((result == XFER_RESULT_SUCCESS) && (xferred_bytes > 0) &&
|
||||
((xferred_bytes & (_btd_itf.ep_acl_in_pkt_sz - 1)) == 0)) {
|
||||
// Save number of transferred bytes
|
||||
_btd_itf.prev_xferred_bytes = xferred_bytes;
|
||||
|
||||
// Send zero-length packet
|
||||
tud_bt_acl_data_send(NULL, 0);
|
||||
} else {
|
||||
if (xferred_bytes == 0) {
|
||||
xferred_bytes = _btd_itf.prev_xferred_bytes;
|
||||
_btd_itf.prev_xferred_bytes = 0;
|
||||
}
|
||||
tud_bt_acl_data_sent_cb((uint16_t) xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
117
managed_components/espressif__tinyusb/src/class/bth/bth_device.h
Normal file
117
managed_components/espressif__tinyusb/src/class/bth/bth_device.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
// Allow BTH class to work in historically compatibility mode where the bRequest is always 0xe0.
|
||||
// See Bluetooth Core v5.3, Vol. 4, Part B, Section 2.2
|
||||
#ifndef CFG_TUD_BTH_HISTORICAL_COMPATIBLE
|
||||
#define CFG_TUD_BTH_HISTORICAL_COMPATIBLE 0
|
||||
#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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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);
|
||||
bool btd_deinit (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_ */
|
||||
472
managed_components/espressif__tinyusb/src/class/cdc/cdc.h
Normal file
472
managed_components/espressif__tinyusb/src/class/cdc/cdc.h
Normal file
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup group_class
|
||||
* \defgroup ClassDriver_CDC Communication Device Class (CDC)
|
||||
* Currently only Abstract Control Model subclass is supported
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CDC_H__
|
||||
#define _TUSB_CDC_H__
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup ClassDriver_CDC_Common Common Definitions
|
||||
* @{ */
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC Communication Interface Class
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// Communication Interface Subclass Codes
|
||||
typedef enum
|
||||
{
|
||||
CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2]
|
||||
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL = 0x02 , ///< Abstract Control Model [USBPSTN1.2]
|
||||
CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL = 0x03 , ///< Telephone Control Model [USBPSTN1.2]
|
||||
CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL = 0x04 , ///< Multi-Channel Control Model [USBISDN1.2]
|
||||
CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL = 0x05 , ///< CAPI Control Model [USBISDN1.2]
|
||||
CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL = 0x06 , ///< Ethernet Networking Control Model [USBECM1.2]
|
||||
CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL = 0x07 , ///< ATM Networking Control Model [USBATM1.2]
|
||||
CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL = 0x08 , ///< Wireless Handset Control Model [USBWMC1.1]
|
||||
CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT = 0x09 , ///< Device Management [USBWMC1.1]
|
||||
CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL = 0x0A , ///< Mobile Direct Line Model [USBWMC1.1]
|
||||
CDC_COMM_SUBCLASS_OBEX = 0x0B , ///< OBEX [USBWMC1.1]
|
||||
CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL = 0x0C , ///< Ethernet Emulation Model [USBEEM1.0]
|
||||
CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL = 0x0D ///< Network Control Model [USBNCM1.0]
|
||||
} cdc_comm_sublcass_type_t;
|
||||
|
||||
/// Communication Interface Protocol Codes
|
||||
typedef enum
|
||||
{
|
||||
CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND = 0x01 , ///< AT Commands: V.250 etc
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 = 0x02 , ///< AT Commands defined by PCCA-101
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO = 0x03 , ///< AT Commands defined by PCCA-101 & Annex O
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 = 0x04 , ///< AT Commands defined by GSM 07.07
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 = 0x05 , ///< AT Commands defined by 3GPP 27.007
|
||||
CDC_COMM_PROTOCOL_ATCOMMAND_CDMA = 0x06 , ///< AT Commands defined by TIA for CDMA
|
||||
CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL = 0x07 ///< Ethernet Emulation Model
|
||||
} cdc_comm_protocol_type_t;
|
||||
|
||||
//------------- SubType Descriptor in COMM Functional Descriptor -------------//
|
||||
/// Communication Interface SubType Descriptor
|
||||
typedef enum
|
||||
{
|
||||
CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface.
|
||||
CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor.
|
||||
CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor.
|
||||
CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor.
|
||||
CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor.
|
||||
CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor.
|
||||
CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor
|
||||
CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor
|
||||
CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor
|
||||
CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor
|
||||
CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor
|
||||
CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor
|
||||
CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor
|
||||
CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor
|
||||
CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor
|
||||
CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor
|
||||
CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor
|
||||
CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor
|
||||
CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor
|
||||
CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor
|
||||
CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor
|
||||
CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor
|
||||
CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor
|
||||
CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor
|
||||
CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor
|
||||
CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 , ///< OBEX Service Identifier Functional Descriptor
|
||||
CDC_FUNC_DESC_NCM = 0x1A , ///< NCM Functional Descriptor
|
||||
}cdc_func_desc_type_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC Data Interface Class
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// SUBCLASS code of Data Interface is not used and should/must be zero
|
||||
|
||||
// Data Interface Protocol Codes
|
||||
typedef enum{
|
||||
CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI
|
||||
CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC
|
||||
CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent
|
||||
CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol
|
||||
CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931
|
||||
CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol
|
||||
CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures
|
||||
CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control
|
||||
CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN
|
||||
CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands
|
||||
CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack.
|
||||
CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface
|
||||
}cdc_data_protocol_type_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Management Element Request (Control Endpoint)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// Communication Interface Management Element Request Codes
|
||||
typedef enum {
|
||||
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
|
||||
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
|
||||
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
|
||||
CDC_REQUEST_GET_COMM_FEATURE = 0x03,
|
||||
CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04,
|
||||
|
||||
CDC_REQUEST_SET_AUX_LINE_STATE = 0x10,
|
||||
CDC_REQUEST_SET_HOOK_STATE = 0x11,
|
||||
CDC_REQUEST_PULSE_SETUP = 0x12,
|
||||
CDC_REQUEST_SEND_PULSE = 0x13,
|
||||
CDC_REQUEST_SET_PULSE_TIME = 0x14,
|
||||
CDC_REQUEST_RING_AUX_JACK = 0x15,
|
||||
|
||||
CDC_REQUEST_SET_LINE_CODING = 0x20,
|
||||
CDC_REQUEST_GET_LINE_CODING = 0x21,
|
||||
CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22,
|
||||
CDC_REQUEST_SEND_BREAK = 0x23,
|
||||
|
||||
CDC_REQUEST_SET_RINGER_PARMS = 0x30,
|
||||
CDC_REQUEST_GET_RINGER_PARMS = 0x31,
|
||||
CDC_REQUEST_SET_OPERATION_PARMS = 0x32,
|
||||
CDC_REQUEST_GET_OPERATION_PARMS = 0x33,
|
||||
CDC_REQUEST_SET_LINE_PARMS = 0x34,
|
||||
CDC_REQUEST_GET_LINE_PARMS = 0x35,
|
||||
CDC_REQUEST_DIAL_DIGITS = 0x36,
|
||||
CDC_REQUEST_SET_UNIT_PARAMETER = 0x37,
|
||||
CDC_REQUEST_GET_UNIT_PARAMETER = 0x38,
|
||||
CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39,
|
||||
CDC_REQUEST_GET_PROFILE = 0x3A,
|
||||
|
||||
CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40,
|
||||
CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
|
||||
CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
|
||||
CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43,
|
||||
CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44,
|
||||
|
||||
CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50,
|
||||
CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51,
|
||||
CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52,
|
||||
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
|
||||
|
||||
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
|
||||
} cdc_management_request_t;
|
||||
|
||||
typedef enum {
|
||||
CDC_CONTROL_LINE_STATE_DTR = 0x01,
|
||||
CDC_CONTROL_LINE_STATE_RTS = 0x02,
|
||||
} cdc_control_line_state_t;
|
||||
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
|
||||
CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
|
||||
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
|
||||
} cdc_line_coding_stopbits_t;
|
||||
|
||||
#define CDC_LINE_CODING_STOP_BITS_TEXT(STOP_BITS) ( \
|
||||
STOP_BITS == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \
|
||||
STOP_BITS == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \
|
||||
STOP_BITS == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?" )
|
||||
|
||||
// TODO Backward compatible for typos. Maybe removed in the future release
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
|
||||
#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
|
||||
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_PARITY_NONE = 0,
|
||||
CDC_LINE_CODING_PARITY_ODD = 1,
|
||||
CDC_LINE_CODING_PARITY_EVEN = 2,
|
||||
CDC_LINE_CODING_PARITY_MARK = 3,
|
||||
CDC_LINE_CODING_PARITY_SPACE = 4,
|
||||
} cdc_line_coding_parity_t;
|
||||
|
||||
#define CDC_LINE_CODING_PARITY_CHAR(PARITY) ( \
|
||||
PARITY == CDC_LINE_CODING_PARITY_NONE ? 'N' : \
|
||||
PARITY == CDC_LINE_CODING_PARITY_ODD ? 'O' : \
|
||||
PARITY == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \
|
||||
PARITY == CDC_LINE_CODING_PARITY_MARK ? 'M' : \
|
||||
PARITY == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?' )
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Management Element Notification (Notification Endpoint)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define CDC_REQ_TYPE_NOTIF 0xA1 ///< Direction IN; Type Class; Recipient Interface
|
||||
|
||||
/// 6.3 Notification Codes
|
||||
typedef enum {
|
||||
CDC_NOTIF_NETWORK_CONNECTION = 0x00, // notify the host about network connection status.
|
||||
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, // notify the host that a response is available.
|
||||
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
|
||||
CDC_NOTIF_RING_DETECT = 0x09,
|
||||
CDC_NOTIF_SERIAL_STATE = 0x20,
|
||||
CDC_NOTIF_CALL_STATE_CHANGE = 0x28,
|
||||
CDC_NOTIF_LINE_STATE_CHANGE = 0x29,
|
||||
CDC_NOTIF_CONNECTION_SPEED_CHANGE = 0x2A, // notify the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
|
||||
CDC_NOTIF_MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40,
|
||||
} cdc_notify_request_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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 {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_
|
||||
uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal
|
||||
}cdc_desc_func_header_t;
|
||||
|
||||
/// Union Functional Descriptor (Communication Interface)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
uint8_t bControlInterface ; ///< Interface number of Communication Interface
|
||||
uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface
|
||||
}cdc_desc_func_union_t;
|
||||
|
||||
#define cdc_desc_func_union_n_t(no_slave)\
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength ;\
|
||||
uint8_t bDescriptorType ;\
|
||||
uint8_t bDescriptorSubType ;\
|
||||
uint8_t bControlInterface ;\
|
||||
uint8_t bSubordinateInterface[no_slave] ;\
|
||||
}
|
||||
|
||||
/// Country Selection Functional Descriptor (Communication Interface)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes.
|
||||
uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country.
|
||||
} cdc_desc_func_country_selection_t;
|
||||
|
||||
#define cdc_desc_func_country_selection_n_t(no_country) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength ;\
|
||||
uint8_t bDescriptorType ;\
|
||||
uint8_t bDescriptorSubType ;\
|
||||
uint8_t iCountryCodeRelDate ;\
|
||||
uint16_t wCountryCode[no_country] ;\
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// \brief Call Management Functional Descriptor
|
||||
/// \details This functional descriptor describes the processing of calls for the Communications Class interface.
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
|
||||
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 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 TU_RESERVED : 4;
|
||||
}cdc_acm_capability_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler");
|
||||
|
||||
/// 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.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
cdc_acm_capability_t bmCapabilities ;
|
||||
}cdc_desc_func_acm_t;
|
||||
|
||||
/// \brief Direct Line Management Functional Descriptor
|
||||
/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
struct {
|
||||
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 TU_RESERVED : 5;
|
||||
} bmCapabilities;
|
||||
}cdc_desc_func_direct_line_management_t;
|
||||
|
||||
/// \brief Telephone Ringer Functional Descriptor
|
||||
/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface,
|
||||
/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
uint8_t bRingerVolSteps ;
|
||||
uint8_t bNumRingerPatterns ;
|
||||
}cdc_desc_func_telephone_ringer_t;
|
||||
|
||||
/// \brief Telephone Operational Modes Functional Descriptor
|
||||
/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by
|
||||
/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
struct {
|
||||
uint8_t simple_mode : 1;
|
||||
uint8_t standalone_mode : 1;
|
||||
uint8_t computer_centric_mode : 1;
|
||||
uint8_t TU_RESERVED : 5;
|
||||
} bmCapabilities;
|
||||
}cdc_desc_func_telephone_operational_modes_t;
|
||||
|
||||
/// \brief Telephone Call and Line State Reporting Capabilities Descriptor
|
||||
/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a
|
||||
/// telephone device to report optional call and line states.
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
|
||||
struct {
|
||||
uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone
|
||||
uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states.
|
||||
uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information.
|
||||
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 TU_RESERVED0 : 2;
|
||||
uint32_t TU_RESERVED1 : 16;
|
||||
uint32_t TU_RESERVED2 : 8;
|
||||
} bmCapabilities;
|
||||
}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
|
||||
|
||||
// TODO remove
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc) {
|
||||
return p_desc[2];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Requests
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t bit_rate;
|
||||
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
|
||||
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
|
||||
uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
|
||||
} cdc_line_coding_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
|
||||
|
||||
typedef union TU_ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t dtr : 1;
|
||||
uint8_t rts : 1;
|
||||
uint8_t : 6;
|
||||
};
|
||||
uint8_t value;
|
||||
} cdc_line_control_state_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 1, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Notifications
|
||||
//--------------------------------------------------------------------+
|
||||
// PSTN 1.2 section 6.5.4 table 31
|
||||
typedef union TU_ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t bRxCarrier : 1; // DCD
|
||||
uint16_t bTxCarrier : 1; // DSR
|
||||
uint16_t bBreak : 1; // Break Detected
|
||||
uint16_t bRingSignal : 1;
|
||||
uint16_t bFraming : 1;
|
||||
uint16_t bParity : 1;
|
||||
uint16_t bOverRun : 1;
|
||||
uint16_t : 9;
|
||||
};
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t dcd : 1;
|
||||
uint16_t dsr : 1;
|
||||
uint16_t brk : 1;
|
||||
uint16_t :13;
|
||||
};
|
||||
uint16_t value;
|
||||
} cdc_notify_uart_state_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_notify_uart_state_t) == 2, "size is not correct");
|
||||
|
||||
// CDC 1.2 section 6.3.3 table 21
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t upstream_bitrate;
|
||||
uint32_t downstream_bitrate;
|
||||
} cdc_notify_conn_speed_change_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
tusb_control_request_t request;
|
||||
union {
|
||||
cdc_notify_uart_state_t serial_state;
|
||||
cdc_notify_conn_speed_change_t conn_speed_change;
|
||||
};
|
||||
} cdc_notify_msg_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(cdc_notify_msg_t) == 16, "size is not correct");
|
||||
|
||||
TU_ATTR_PACKED_END // End of all packed definitions
|
||||
TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
577
managed_components/espressif__tinyusb/src/class/cdc/cdc_device.c
Normal file
577
managed_components/espressif__tinyusb/src/class/cdc/cdc_device.c
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_CDC)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "cdc_device.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_CDC_LOG_LEVEL
|
||||
#define CFG_TUD_CDC_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_CDC_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
typedef struct {
|
||||
uint8_t rhport;
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
uint8_t ep_notify;
|
||||
uint8_t line_state; // Bit 0: DTR, Bit 1: RTS
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
char wanted_char;
|
||||
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
|
||||
|
||||
// FIFO
|
||||
tu_fifo_t rx_ff;
|
||||
tu_fifo_t tx_ff;
|
||||
|
||||
uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
|
||||
|
||||
OSAL_MUTEX_DEF(rx_ff_mutex);
|
||||
OSAL_MUTEX_DEF(tx_ff_mutex);
|
||||
} cdcd_interface_t;
|
||||
|
||||
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
|
||||
TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
|
||||
|
||||
#if CFG_TUD_CDC_NOTIFY
|
||||
TUD_EPBUF_TYPE_DEF(cdc_notify_msg_t, epnotify);
|
||||
#endif
|
||||
} cdcd_epbuf_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
|
||||
CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
|
||||
|
||||
static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
|
||||
|
||||
static bool _prep_out_transaction(uint8_t itf) {
|
||||
const uint8_t rhport = 0;
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
|
||||
|
||||
// Skip if usb is not ready yet
|
||||
TU_VERIFY(tud_ready() && p_cdc->ep_out);
|
||||
|
||||
uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
||||
// and slowly move it to the FIFO when read().
|
||||
// This pre-check reduces endpoint claiming
|
||||
TU_VERIFY(available >= CFG_TUD_CDC_EP_BUFSIZE);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_out));
|
||||
|
||||
// fifo can be changed before endpoint is claimed
|
||||
available = tu_fifo_remaining(&p_cdc->rx_ff);
|
||||
|
||||
if (available >= CFG_TUD_CDC_EP_BUFSIZE) {
|
||||
return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
|
||||
} else {
|
||||
// Release endpoint since we don't make any transfer
|
||||
usbd_edpt_release(p_cdc->rhport, p_cdc->ep_out);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf) {
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) {
|
||||
(void) itf;
|
||||
(void) wanted_char;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf) {
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_notify_complete_cb(uint8_t itf) {
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
(void) itf;
|
||||
(void) dtr;
|
||||
(void) rts;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) {
|
||||
(void) itf;
|
||||
(void) p_line_coding;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
|
||||
(void) itf;
|
||||
(void) duration_ms;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
|
||||
TU_VERIFY(driver_cfg);
|
||||
_cdcd_cfg = *driver_cfg;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_cdc_n_ready(uint8_t itf) {
|
||||
return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0;
|
||||
}
|
||||
|
||||
bool tud_cdc_n_connected(uint8_t itf) {
|
||||
// DTR (bit 0) active is considered as connected
|
||||
return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
|
||||
}
|
||||
|
||||
uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
|
||||
return _cdcd_itf[itf].line_state;
|
||||
}
|
||||
|
||||
void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
|
||||
(*coding) = _cdcd_itf[itf].line_coding;
|
||||
}
|
||||
|
||||
#if CFG_TUD_CDC_NOTIFY
|
||||
bool tud_cdc_n_notify_uart_state (uint8_t itf, const cdc_notify_uart_state_t *state) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
|
||||
TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
|
||||
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));
|
||||
|
||||
cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
|
||||
notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
|
||||
notify_msg->request.bRequest = CDC_NOTIF_SERIAL_STATE;
|
||||
notify_msg->request.wValue = 0;
|
||||
notify_msg->request.wIndex = p_cdc->itf_num;
|
||||
notify_msg->request.wLength = sizeof(cdc_notify_uart_state_t);
|
||||
notify_msg->serial_state = *state;
|
||||
|
||||
return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_uart_state_t));
|
||||
}
|
||||
|
||||
bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
|
||||
TU_VERIFY(tud_ready() && p_cdc->ep_notify != 0);
|
||||
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notify));
|
||||
|
||||
cdc_notify_msg_t* notify_msg = &p_epbuf->epnotify;
|
||||
notify_msg->request.bmRequestType = CDC_REQ_TYPE_NOTIF;
|
||||
notify_msg->request.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE;
|
||||
notify_msg->request.wValue = 0;
|
||||
notify_msg->request.wIndex = p_cdc->itf_num;
|
||||
notify_msg->request.wLength = sizeof(cdc_notify_conn_speed_change_t);
|
||||
notify_msg->conn_speed_change = *conn_speed_change;
|
||||
|
||||
return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notify, (uint8_t *)notify_msg, 8 + sizeof(cdc_notify_conn_speed_change_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
|
||||
_cdcd_itf[itf].wanted_char = wanted;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// READ API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_cdc_n_available(uint8_t itf) {
|
||||
return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
|
||||
}
|
||||
|
||||
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
_prep_out_transaction(itf);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
|
||||
return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
|
||||
}
|
||||
|
||||
void tud_cdc_n_read_flush(uint8_t itf) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
tu_fifo_clear(&p_cdc->rx_ff);
|
||||
_prep_out_transaction(itf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// WRITE API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
uint16_t wr_count = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
|
||||
// flush if queue more than packet size
|
||||
if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
|
||||
#if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
|
||||
|| tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
|
||||
#endif
|
||||
) {
|
||||
tud_cdc_n_write_flush(itf);
|
||||
}
|
||||
|
||||
return wr_count;
|
||||
}
|
||||
|
||||
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||
cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
|
||||
TU_VERIFY(tud_ready(), 0); // Skip if usb is not ready yet
|
||||
|
||||
// No data to send
|
||||
if (!tu_fifo_count(&p_cdc->tx_ff)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_in), 0); // Claim the endpoint
|
||||
|
||||
// Pull data from FIFO
|
||||
const uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
|
||||
|
||||
if (count) {
|
||||
TU_ASSERT(usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_in, p_epbuf->epin, count), 0);
|
||||
return count;
|
||||
} else {
|
||||
// Release endpoint since we don't make any transfer
|
||||
// Note: data is dropped if terminal is not connected
|
||||
usbd_edpt_release(p_cdc->rhport, p_cdc->ep_in);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
//--------------------------------------------------------------------+
|
||||
void cdcd_init(void) {
|
||||
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
|
||||
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
|
||||
p_cdc->wanted_char = (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.stop_bits = 0;
|
||||
p_cdc->line_coding.parity = 0;
|
||||
p_cdc->line_coding.data_bits = 8;
|
||||
|
||||
// Config RX fifo
|
||||
tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
|
||||
|
||||
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
|
||||
// know if data is actually polled by terminal. This way the most current data is prioritized.
|
||||
// Default: is overwritable
|
||||
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, _cdcd_cfg.tx_overwritabe_if_not_connected);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
|
||||
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool cdcd_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cdcd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
|
||||
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
|
||||
if (!_cdcd_cfg.rx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->rx_ff);
|
||||
}
|
||||
if (!_cdcd_cfg.tx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->tx_ff);
|
||||
}
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
|
||||
// Only support ACM subclass
|
||||
TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
|
||||
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
|
||||
|
||||
// Find available interface
|
||||
cdcd_interface_t* p_cdc;
|
||||
uint8_t cdc_id;
|
||||
for (cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
|
||||
p_cdc = &_cdcd_itf[cdc_id];
|
||||
if (p_cdc->ep_in == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(cdc_id < CFG_TUD_CDC, 0);
|
||||
|
||||
//------------- Control Interface -------------//
|
||||
p_cdc->rhport = rhport;
|
||||
p_cdc->itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||
const uint8_t* p_desc = tu_desc_next(itf_desc);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
|
||||
// notification endpoint
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||
p_cdc->ep_notify = desc_ep->bEndpointAddress;
|
||||
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
//------------- Data Interface (if any) -------------//
|
||||
if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
|
||||
(TUSB_CLASS_CDC_DATA == ((const tusb_desc_interface_t*) p_desc)->bInterfaceClass)) {
|
||||
// next to endpoint descriptor
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
// Open endpoint pair
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
|
||||
|
||||
drv_len += 2 * sizeof(tusb_desc_endpoint_t);
|
||||
}
|
||||
|
||||
// Prepare for incoming data
|
||||
_prep_out_transaction(cdc_id);
|
||||
|
||||
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 cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
|
||||
// Handle class request only
|
||||
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||
|
||||
uint8_t itf;
|
||||
cdcd_interface_t* p_cdc;
|
||||
|
||||
// Identify which interface to use
|
||||
for (itf = 0; itf < CFG_TUD_CDC; itf++) {
|
||||
p_cdc = &_cdcd_itf[itf];
|
||||
if (p_cdc->itf_num == request->wIndex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_VERIFY(itf < CFG_TUD_CDC);
|
||||
|
||||
switch (request->bRequest) {
|
||||
case CDC_REQUEST_SET_LINE_CODING:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_LOG_DRV(" 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) {
|
||||
tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
|
||||
}
|
||||
break;
|
||||
|
||||
case CDC_REQUEST_GET_LINE_CODING:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_LOG_DRV(" 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:
|
||||
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);
|
||||
|
||||
p_cdc->line_state = (uint8_t) request->wValue;
|
||||
|
||||
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
|
||||
if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
|
||||
} else {
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, false);
|
||||
}
|
||||
|
||||
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
|
||||
|
||||
// Invoke callback
|
||||
tud_cdc_line_state_cb(itf, dtr, rts);
|
||||
}
|
||||
break;
|
||||
|
||||
case CDC_REQUEST_SEND_BREAK:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
tud_control_status(rhport, request);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
TU_LOG_DRV(" Send Break\r\n");
|
||||
tud_cdc_send_break_cb(itf, request->wValue);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
uint8_t itf;
|
||||
cdcd_interface_t* p_cdc;
|
||||
|
||||
// Identify which interface to use
|
||||
for (itf = 0; itf < CFG_TUD_CDC; itf++) {
|
||||
p_cdc = &_cdcd_itf[itf];
|
||||
if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in) || (ep_addr == p_cdc->ep_notify)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(itf < CFG_TUD_CDC);
|
||||
cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
|
||||
|
||||
// Received new data
|
||||
if (ep_addr == p_cdc->ep_out) {
|
||||
tu_fifo_write_n(&p_cdc->rx_ff, p_epbuf->epout, (uint16_t) xferred_bytes);
|
||||
|
||||
// Check for wanted char and invoke callback if needed
|
||||
if (((signed char) p_cdc->wanted_char) != -1) {
|
||||
for (uint32_t i = 0; i < xferred_bytes; i++) {
|
||||
if ((p_cdc->wanted_char == p_epbuf->epout[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) {
|
||||
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invoke receive callback (if there is still data)
|
||||
if (!tu_fifo_empty(&p_cdc->rx_ff)) {
|
||||
tud_cdc_rx_cb(itf);
|
||||
}
|
||||
|
||||
// prepare for OUT transaction
|
||||
_prep_out_transaction(itf);
|
||||
}
|
||||
|
||||
// Data sent to host, we continue to fetch from tx fifo to send.
|
||||
// Note: This will cause incorrect baudrate set in line coding.
|
||||
// Though maybe the baudrate is not really important !!!
|
||||
if (ep_addr == p_cdc->ep_in) {
|
||||
// invoke transmit callback to possibly refill tx fifo
|
||||
tud_cdc_tx_complete_cb(itf);
|
||||
|
||||
if (0 == tud_cdc_n_write_flush(itf)) {
|
||||
// If there is no data left, a ZLP should be sent if
|
||||
// xferred_bytes is multiple of EP Packet size and not zero
|
||||
if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
|
||||
if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sent notification to host
|
||||
if (ep_addr == p_cdc->ep_notify) {
|
||||
tud_cdc_notify_complete_cb(itf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
260
managed_components/espressif__tinyusb/src/class/cdc/cdc_device.h
Normal file
260
managed_components/espressif__tinyusb/src/class/cdc/cdc_device.h
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_CDC_DEVICE_H_
|
||||
#define TUSB_CDC_DEVICE_H_
|
||||
|
||||
#include "cdc.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
#ifndef CFG_TUD_CDC_NOTIFY
|
||||
#define CFG_TUD_CDC_NOTIFY 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_CDC_EP_BUFSIZE) && defined(CFG_TUD_CDC_EPSIZE)
|
||||
#warning CFG_TUD_CDC_EPSIZE is renamed to CFG_TUD_CDC_EP_BUFSIZE, please update to use the new name
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE CFG_TUD_CDC_EPSIZE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CDC_EP_BUFSIZE
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
|
||||
uint8_t tx_persistent : 1; // keep tx fifo data even with reset or disconnect
|
||||
uint8_t tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
|
||||
} tud_cdc_configure_t;
|
||||
|
||||
#define TUD_CDC_CONFIGURE_DEFAULT() { \
|
||||
.rx_persistent = 0, \
|
||||
.tx_persistent = 0, \
|
||||
.tx_overwritabe_if_not_connected = 1, \
|
||||
}
|
||||
|
||||
// Configure CDC driver behavior
|
||||
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
|
||||
|
||||
// Backward compatible
|
||||
#define tud_cdc_configure_fifo_t tud_cdc_configure_t
|
||||
#define tud_cdc_configure_fifo tud_cdc_configure
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if interface is ready
|
||||
bool tud_cdc_n_ready(uint8_t itf);
|
||||
|
||||
// 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
|
||||
TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_n_read_char(uint8_t itf) {
|
||||
uint8_t ch;
|
||||
return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
|
||||
}
|
||||
|
||||
// Clear the received FIFO
|
||||
void tud_cdc_n_read_flush(uint8_t itf);
|
||||
|
||||
// Get a byte from FIFO without removing it
|
||||
bool tud_cdc_n_peek(uint8_t itf, uint8_t* ui8);
|
||||
|
||||
// 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);
|
||||
|
||||
// Write a byte
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) {
|
||||
return tud_cdc_n_write(itf, &ch, 1);
|
||||
}
|
||||
|
||||
// Write a null-terminated string
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_str(uint8_t itf, char const* str) {
|
||||
return tud_cdc_n_write(itf, str, strlen(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);
|
||||
|
||||
// Clear the transmit FIFO
|
||||
bool tud_cdc_n_write_clear(uint8_t itf);
|
||||
|
||||
|
||||
#if CFG_TUD_CDC_NOTIFY
|
||||
// Send UART status notification: DCD, DSR etc ..
|
||||
bool tud_cdc_n_notify_uart_state(uint8_t itf, const cdc_notify_uart_state_t *state);
|
||||
|
||||
// Send connection speed change notification
|
||||
bool tud_cdc_n_notify_conn_speed_change(uint8_t itf, const cdc_notify_conn_speed_change_t* conn_speed_change);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_uart_state(const cdc_notify_uart_state_t* state) {
|
||||
return tud_cdc_n_notify_uart_state(0, state);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_notify_conn_speed_change(const cdc_notify_conn_speed_change_t* conn_speed_change) {
|
||||
return tud_cdc_n_notify_conn_speed_change(0, conn_speed_change);
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_ready(void) {
|
||||
return tud_cdc_n_ready(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_connected(void) {
|
||||
return tud_cdc_n_connected(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) {
|
||||
return tud_cdc_n_get_line_state(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) {
|
||||
tud_cdc_n_get_line_coding(0, coding);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_wanted_char(char wanted) {
|
||||
tud_cdc_n_set_wanted_char(0, wanted);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_available(void) {
|
||||
return tud_cdc_n_available(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_read_char(void) {
|
||||
return tud_cdc_n_read_char(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_read(void* buffer, uint32_t bufsize) {
|
||||
return tud_cdc_n_read(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_read_flush(void) {
|
||||
tud_cdc_n_read_flush(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_peek(uint8_t* ui8) {
|
||||
return tud_cdc_n_peek(0, ui8);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_char(char ch) {
|
||||
return tud_cdc_n_write_char(0, ch);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write(void const* buffer, uint32_t bufsize) {
|
||||
return tud_cdc_n_write(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_str(char const* str) {
|
||||
return tud_cdc_n_write_str(0, str);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_flush(void) {
|
||||
return tud_cdc_n_write_flush(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_available(void) {
|
||||
return tud_cdc_n_write_available(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) {
|
||||
return tud_cdc_n_write_clear(0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
void tud_cdc_rx_cb(uint8_t itf);
|
||||
|
||||
// Invoked when received `wanted_char`
|
||||
void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
|
||||
|
||||
// Invoked when a TX is complete and therefore space becomes available in TX buffer
|
||||
void tud_cdc_tx_complete_cb(uint8_t itf);
|
||||
|
||||
// Invoked when a notification is sent to host
|
||||
void tud_cdc_notify_complete_cb(uint8_t itf);
|
||||
|
||||
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
|
||||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
|
||||
|
||||
// Invoked when line coding is change via SET_LINE_CODING
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
|
||||
|
||||
// Invoked when received send break
|
||||
// \param[in] itf interface for which send break was received.
|
||||
// \param[in] duration_ms the length of time, in milliseconds, of the break signal. If a value of FFFFh, then the
|
||||
// device will send a break until another SendBreak request is received with value 0000h.
|
||||
void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void cdcd_init (void);
|
||||
bool cdcd_deinit (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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_DEVICE_H_ */
|
||||
2540
managed_components/espressif__tinyusb/src/class/cdc/cdc_host.c
Normal file
2540
managed_components/espressif__tinyusb/src/class/cdc/cdc_host.c
Normal file
File diff suppressed because it is too large
Load Diff
258
managed_components/espressif__tinyusb/src/class/cdc/cdc_host.h
Normal file
258
managed_components/espressif__tinyusb/src/class/cdc/cdc_host.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CDC_HOST_H_
|
||||
#define _TUSB_CDC_HOST_H_
|
||||
|
||||
#include "cdc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RX FIFO size
|
||||
#ifndef CFG_TUH_CDC_RX_BUFSIZE
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// RX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_RX_EPSIZE
|
||||
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX FIFO size
|
||||
#ifndef CFG_TUH_CDC_TX_BUFSIZE
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_TX_EPSIZE
|
||||
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get Interface index from device address + interface number
|
||||
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||
uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||
|
||||
// Get Interface information
|
||||
// return true if index is correct and interface is currently mounted
|
||||
bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
|
||||
|
||||
// Check if an interface is mounted
|
||||
bool tuh_cdc_mounted(uint8_t idx);
|
||||
|
||||
// Get local (cached) line state
|
||||
// This function should return correct values if tuh_cdc_set_control_line_state() / tuh_cdc_get_control_line_state()
|
||||
// are invoked previously or CFG_TUH_CDC_LINE_STATE_ON_ENUM is defined.
|
||||
bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t* line_state);
|
||||
|
||||
// Get current DTR status
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_get_dtr(uint8_t idx) {
|
||||
uint16_t line_state;
|
||||
TU_VERIFY(tuh_cdc_get_control_line_state_local(idx, &line_state));
|
||||
return (line_state & CDC_CONTROL_LINE_STATE_DTR) != 0;
|
||||
}
|
||||
|
||||
// Get current RTS status
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_get_rts(uint8_t idx) {
|
||||
uint16_t line_state;
|
||||
TU_VERIFY(tuh_cdc_get_control_line_state_local(idx, &line_state));
|
||||
return (line_state & CDC_CONTROL_LINE_STATE_RTS) != 0;
|
||||
}
|
||||
|
||||
// Check if interface is connected (DTR active)
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
|
||||
return tuh_cdc_get_dtr(idx);
|
||||
}
|
||||
|
||||
// Get local (saved/cached) version of line coding.
|
||||
// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
|
||||
// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
|
||||
// NOTE: This function does not make any USB transfer request to device.
|
||||
bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
|
||||
|
||||
#define tuh_cdc_get_local_line_coding tuh_cdc_get_line_coding_local // backward compatibility
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Write API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the number of bytes available for writing
|
||||
uint32_t tuh_cdc_write_available(uint8_t idx);
|
||||
|
||||
// Write to cdc interface
|
||||
uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
|
||||
|
||||
// Force sending data if possible, return number of forced bytes
|
||||
uint32_t tuh_cdc_write_flush(uint8_t idx);
|
||||
|
||||
// Clear the transmit FIFO
|
||||
bool tuh_cdc_write_clear(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Read API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the number of bytes available for reading
|
||||
uint32_t tuh_cdc_read_available(uint8_t idx);
|
||||
|
||||
// Read from cdc interface
|
||||
uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
|
||||
|
||||
// Get a byte from RX FIFO without removing it
|
||||
bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
|
||||
|
||||
// Clear the received FIFO
|
||||
bool tuh_cdc_read_clear (uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Request API
|
||||
// Each Function will make a USB control transfer request to/from device
|
||||
// - If complete_cb is provided, the function will return immediately and invoke
|
||||
// the callback when request is complete.
|
||||
// - If complete_cb is NULL, the function will block until request is complete.
|
||||
// In this case, user_data should be usb_xfer_result_t* to hold the transfer result.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Request to Set Control Line State: DTR (bit 0), RTS (bit 1)
|
||||
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set DTR
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
cdc_line_control_state_t line_state = { .dtr = dtr_state };
|
||||
line_state.rts = tuh_cdc_get_rts(idx);
|
||||
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Request to Set RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
cdc_line_control_state_t line_state = { .rts = rts_state };
|
||||
line_state.dtr = tuh_cdc_get_dtr(idx);
|
||||
return tuh_cdc_set_control_line_state(idx, line_state.value, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Request to set baudrate
|
||||
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to set data format
|
||||
bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set Line Coding = baudrate + data format
|
||||
// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Get Line Coding (ACM only)
|
||||
// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
|
||||
// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined
|
||||
// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding);
|
||||
|
||||
// Connect by set both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Disconnect by clear both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Request Sync API
|
||||
// Each Function will make a USB control transfer request to/from device the function will block until request is
|
||||
// complete. The function will return the transfer request result
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx, uint16_t line_state) {
|
||||
TU_API_SYNC(tuh_cdc_set_control_line_state, idx, line_state);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_dtr_sync(uint8_t idx, bool dtr_state) {
|
||||
TU_API_SYNC(tuh_cdc_set_dtr, idx, dtr_state);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_rts_sync(uint8_t idx, bool rts_state) {
|
||||
TU_API_SYNC(tuh_cdc_set_rts, idx, rts_state);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_baudrate_sync(uint8_t idx, uint32_t baudrate) {
|
||||
TU_API_SYNC(tuh_cdc_set_baudrate, idx, baudrate);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
|
||||
TU_API_SYNC(tuh_cdc_set_data_format, idx, stop_bits, parity, data_bits);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_line_coding_sync(uint8_t idx, cdc_line_coding_t const* line_coding) {
|
||||
TU_API_SYNC(tuh_cdc_set_line_coding, idx, line_coding);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_connect_sync(uint8_t idx) {
|
||||
TU_API_SYNC(tuh_cdc_connect, idx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_disconnect_sync(uint8_t idx) {
|
||||
TU_API_SYNC(tuh_cdc_disconnect, idx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC APPLICATION CALLBACKS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when a device with CDC interface is mounted
|
||||
// idx is index of cdc interface in the internal pool.
|
||||
extern void tuh_cdc_mount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when a device with CDC interface is unmounted
|
||||
extern void tuh_cdc_umount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when received new data
|
||||
extern void tuh_cdc_rx_cb(uint8_t idx);
|
||||
|
||||
// Invoked when a TX is complete and therefore space becomes available in TX buffer
|
||||
extern void tuh_cdc_tx_complete_cb(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
bool cdch_init (void);
|
||||
bool cdch_deinit (void);
|
||||
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void cdch_close (uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_HOST_H_ */
|
||||
301
managed_components/espressif__tinyusb/src/class/cdc/cdc_rndis.h
Normal file
301
managed_components/espressif__tinyusb/src/class/cdc/cdc_rndis.h
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
|
||||
* \defgroup CDC_RNDIS Remote Network Driver Interface Specification (RNDIS)
|
||||
* @{
|
||||
* \defgroup CDC_RNDIS_Common Common Definitions
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CDC_RNDIS_H_
|
||||
#define _TUSB_CDC_RNDIS_H_
|
||||
|
||||
#include "cdc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __CC_ARM
|
||||
#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
|
||||
#endif
|
||||
|
||||
/// RNDIS Message Types
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_MSG_PACKET = 0x00000001UL, ///< The host and device use this to send network data to one another.
|
||||
|
||||
RNDIS_MSG_INITIALIZE = 0x00000002UL, ///< Sent by the host to initialize the device.
|
||||
RNDIS_MSG_INITIALIZE_CMPLT = 0x80000002UL, ///< Device response to an initialize message.
|
||||
|
||||
RNDIS_MSG_HALT = 0x00000003UL, ///< Sent by the host to halt the device. This does not have a response. It is optional for the device to send this message to the host.
|
||||
|
||||
RNDIS_MSG_QUERY = 0x00000004UL, ///< Sent by the host to send a query OID.
|
||||
RNDIS_MSG_QUERY_CMPLT = 0x80000004UL, ///< Device response to a query OID.
|
||||
|
||||
RNDIS_MSG_SET = 0x00000005UL, ///< Sent by the host to send a set OID.
|
||||
RNDIS_MSG_SET_CMPLT = 0x80000005UL, ///< Device response to a set OID.
|
||||
|
||||
RNDIS_MSG_RESET = 0x00000006UL, ///< Sent by the host to perform a soft reset on the device.
|
||||
RNDIS_MSG_RESET_CMPLT = 0x80000006UL, ///< Device response to reset message.
|
||||
|
||||
RNDIS_MSG_INDICATE_STATUS = 0x00000007UL, ///< Sent by the device to indicate its status or an error when an unrecognized message is received.
|
||||
|
||||
RNDIS_MSG_KEEP_ALIVE = 0x00000008UL, ///< During idle periods, sent every few seconds by the host to check that the device is still responsive. It is optional for the device to send this message to check if the host is active.
|
||||
RNDIS_MSG_KEEP_ALIVE_CMPLT = 0x80000008UL ///< The device response to a keepalivemessage. The host can respond with this message to a keepalive message from the device when the device implements the optional KeepAliveTimer.
|
||||
}rndis_msg_type_t;
|
||||
|
||||
/// RNDIS Message Status Values
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_STATUS_SUCCESS = 0x00000000UL, ///< Success
|
||||
RNDIS_STATUS_FAILURE = 0xC0000001UL, ///< Unspecified error
|
||||
RNDIS_STATUS_INVALID_DATA = 0xC0010015UL, ///< Invalid data error
|
||||
RNDIS_STATUS_NOT_SUPPORTED = 0xC00000BBUL, ///< Unsupported request error
|
||||
RNDIS_STATUS_MEDIA_CONNECT = 0x4001000BUL, ///< Device is connected to a network medium.
|
||||
RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000CUL ///< Device is disconnected from the medium.
|
||||
}rndis_msg_status_t;
|
||||
|
||||
#ifdef __CC_ARM
|
||||
#pragma diag_default 66 // return Keil 66 to normal severity
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MESSAGE STRUCTURE
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Initialize -------------//
|
||||
/// \brief Initialize Message
|
||||
/// \details This message MUST be sent by the host to initialize the device.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message type, must be \ref RNDIS_MSG_INITIALIZE
|
||||
uint32_t length ; ///< Message length in bytes, must be 0x18
|
||||
uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
|
||||
uint32_t major_version ; ///< The major version of the RNDIS Protocol implemented by the host.
|
||||
uint32_t minor_version ; ///< The minor version of the RNDIS Protocol implemented by the host
|
||||
uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the host expects to receive from the device.
|
||||
}rndis_msg_initialize_t;
|
||||
|
||||
/// \brief Initialize Complete Message
|
||||
/// \details This message MUST be sent by the device in response to an initialize message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_INITIALIZE_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, must be 0x30
|
||||
uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_initialize_t to which this message is a response.
|
||||
uint32_t status ; ///< The initialization status of the device, has value from \ref rndis_msg_status_t
|
||||
uint32_t major_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
|
||||
uint32_t minor_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
|
||||
uint32_t device_flags ; ///< MUST be set to 0x000000010. Other values are reserved for future use.
|
||||
uint32_t medium ; ///< is 0x00 for RNDIS_MEDIUM_802_3
|
||||
uint32_t max_packet_per_xfer ; ///< The maximum number of concatenated \ref RNDIS_MSG_PACKET messages that the device can handle in a single bus transfer to it. This value MUST be at least 1.
|
||||
uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the device expects to receive from the host.
|
||||
uint32_t packet_alignment_factor ; ///< The byte alignment the device expects for each RNDIS message that is part of a multimessage transfer to it. The value is specified as an exponent of 2; for example, the host uses 2<SUP>{PacketAlignmentFactor}</SUP> as the alignment value.
|
||||
uint32_t reserved[2] ;
|
||||
} rndis_msg_initialize_cmplt_t;
|
||||
|
||||
//------------- Query -------------//
|
||||
/// \brief Query Message
|
||||
/// \details This message MUST be sent by the host to query an OID.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY
|
||||
uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
|
||||
uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
|
||||
uint32_t oid ; ///< The integer value of the host operating system-defined identifier, for the parameter of the device being queried for.
|
||||
uint32_t buffer_length ; ///< The length, in bytes, of the input data required for the OID query. This MUST be set to 0 when there is no input data associated with the OID.
|
||||
uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the input data for the query is located in the message. This value MUST be set to 0 when there is no input data associated with the OID.
|
||||
uint32_t reserved ;
|
||||
uint8_t oid_buffer[] ; ///< Flexible array contains the input data supplied by the host, required for the OID query request processing by the device, as per the host NDIS specification.
|
||||
} rndis_msg_query_t, rndis_msg_set_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(rndis_msg_query_t) == 28, "Make sure flexible array member does not affect layout");
|
||||
|
||||
/// \brief Query Complete Message
|
||||
/// \details This message MUST be sent by the device in response to a query OID message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
|
||||
uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_query_t to which this message is a response.
|
||||
uint32_t status ; ///< The status of processing for the query request, has value from \ref rndis_msg_status_t.
|
||||
uint32_t buffer_length ; ///< The length, in bytes, of the data in the response to the query. This MUST be set to 0 when there is no OIDInputBuffer.
|
||||
uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the response data for the query is located in the message. This MUST be set to 0 when there is no \ref oid_buffer.
|
||||
uint8_t oid_buffer[] ; ///< Flexible array member contains the response data to the OID query request as specified by the host.
|
||||
} rndis_msg_query_cmplt_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(rndis_msg_query_cmplt_t) == 24, "Make sure flexible array member does not affect layout");
|
||||
|
||||
//------------- Reset -------------//
|
||||
/// \brief Reset Message
|
||||
/// \details This message MUST be sent by the host to perform a soft reset on the device.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x06
|
||||
uint32_t reserved ;
|
||||
} rndis_msg_reset_t;
|
||||
|
||||
/// \brief Reset Complete Message
|
||||
/// \details This message MUST be sent by the device in response to a reset message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t status ; ///< The status of processing for the \ref rndis_msg_reset_t, has value from \ref rndis_msg_status_t.
|
||||
uint32_t addressing_reset ; ///< This field indicates whether the addressing information, which is the multicast address list or packet filter, has been lost during the reset operation. This MUST be set to 0x00000001 if the device requires that the host to resend addressing information or MUST be set to zero otherwise.
|
||||
} rndis_msg_reset_cmplt_t;
|
||||
|
||||
//typedef struct {
|
||||
// uint32_t type;
|
||||
// uint32_t length;
|
||||
// uint32_t status;
|
||||
// uint32_t buffer_length;
|
||||
// uint32_t buffer_offset;
|
||||
// uint32_t diagnostic_status; // optional
|
||||
// uint32_t diagnostic_error_offset; // optional
|
||||
// uint32_t status_buffer[0]; // optional
|
||||
//} rndis_msg_indicate_status_t;
|
||||
|
||||
/// \brief Keep Alive Message
|
||||
/// \details This message MUST be sent by the host to check that device is still responsive. It is optional for the device to send this message to check if the host is active
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t request_id ;
|
||||
} rndis_msg_keep_alive_t, rndis_msg_halt_t;
|
||||
|
||||
/// \brief Set Complete Message
|
||||
/// \brief This message MUST be sent in response to a the request message
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t request_id ; ///< must be the same as requesting message
|
||||
uint32_t status ; ///< The status of processing for the request message request by the device to which this message is the response.
|
||||
} rndis_msg_set_cmplt_t, rndis_msg_keep_alive_cmplt_t;
|
||||
|
||||
/// \brief Packet Data Message
|
||||
/// \brief This message MUST be used by the host and the device to send network data to one another.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_PACKET
|
||||
uint32_t length ; ///< Message length in bytes, The total length of this RNDIS message including the header, payload, and padding.
|
||||
uint32_t data_offset ; ///< Specifies the offset, in bytes, from the start of this \a data_offset field of this message to the start of the data. This MUST be an integer multiple of 4.
|
||||
uint32_t data_length ; ///< Specifies the number of bytes in the payload of this message.
|
||||
uint32_t out_of_band_data_offet ; ///< Specifies the offset, in bytes, of the first out-of-band data record from the start of the DataOffset field in this message. MUST be an integer multiple of 4 when out-of-band data is present or set to 0 otherwise. When there are multiple out-ofband data records, each subsequent record MUST immediately follow the previous out-of-band data record.
|
||||
uint32_t out_of_band_data_length ; ///< Specifies, in bytes, the total length of the out-of-band data.
|
||||
uint32_t num_out_of_band_data_elements ; ///< Specifies the number of out-of-band records in this message.
|
||||
uint32_t per_packet_info_offset ; ///< Specifies the offset, in bytes, of the start of per-packet-info data record from the start of the \a data_offset field in this message. MUST be an integer multiple of 4 when per-packet-info data record is present or MUST be set to 0 otherwise. When there are multiple per-packet-info data records, each subsequent record MUST immediately follow the previous record.
|
||||
uint32_t per_packet_info_length ; ///< Specifies, in bytes, the total length of per-packetinformation contained in this message.
|
||||
uint32_t reserved[2] ;
|
||||
uint32_t payload[0] ; ///< Network data contained in this message.
|
||||
|
||||
// uint8_t padding[0]
|
||||
// Additional bytes of zeros added at the end of the message to comply with
|
||||
// the internal and external padding requirements. Internal padding SHOULD be as per the
|
||||
// specification of the out-of-band data record and per-packet-info data record. The external
|
||||
//padding size SHOULD be determined based on the PacketAlignmentFactor field specification
|
||||
//in REMOTE_NDIS_INITIALIZE_CMPLT message by the device, when multiple
|
||||
//REMOTE_NDIS_PACKET_MSG messages are bundled together in a single bus-native message.
|
||||
//In this case, all but the very last REMOTE_NDIS_PACKET_MSG MUST respect the
|
||||
//PacketAlignmentFactor field.
|
||||
|
||||
// rndis_msg_packet_t [0] : (optional) more packet if multiple packet per bus transaction is supported
|
||||
} rndis_msg_packet_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t size ; ///< Length, in bytes, of this header and appended data and padding. This value MUST be an integer multiple of 4.
|
||||
uint32_t type ; ///< MUST be as per host operating system specification.
|
||||
uint32_t offset ; ///< The byte offset from the beginning of this record to the beginning of data.
|
||||
uint32_t data[0] ; ///< Flexible array contains data
|
||||
} rndis_msg_out_of_band_data_t, rndis_msg_per_packet_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// NDIS Object ID
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// NDIS Object ID
|
||||
typedef enum
|
||||
{
|
||||
//------------- General Required OIDs -------------//
|
||||
RNDIS_OID_GEN_SUPPORTED_LIST = 0x00010101, ///< List of supported OIDs
|
||||
RNDIS_OID_GEN_HARDWARE_STATUS = 0x00010102, ///< Hardware status
|
||||
RNDIS_OID_GEN_MEDIA_SUPPORTED = 0x00010103, ///< Media types supported (encoded)
|
||||
RNDIS_OID_GEN_MEDIA_IN_USE = 0x00010104, ///< Media types in use (encoded)
|
||||
RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, ///<
|
||||
RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, ///< Maximum frame size in bytes
|
||||
RNDIS_OID_GEN_LINK_SPEED = 0x00010107, ///< Link speed in units of 100 bps
|
||||
RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, ///< Transmit buffer space
|
||||
RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, ///< Receive buffer space
|
||||
RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010A, ///< Minimum amount of storage, in bytes, that a single packet occupies in the transmit buffer space of the NIC
|
||||
RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010B, ///< Amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC
|
||||
RNDIS_OID_GEN_VENDOR_ID = 0x0001010C, ///< Vendor NIC code
|
||||
RNDIS_OID_GEN_VENDOR_DESCRIPTION = 0x0001010D, ///< Vendor network card description
|
||||
RNDIS_OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E, ///< Current packet filter (encoded)
|
||||
RNDIS_OID_GEN_CURRENT_LOOKAHEAD = 0x0001010F, ///< Current lookahead size in bytes
|
||||
RNDIS_OID_GEN_DRIVER_VERSION = 0x00010110, ///< NDIS version number used by the driver
|
||||
RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, ///< Maximum total packet length in bytes
|
||||
RNDIS_OID_GEN_PROTOCOL_OPTIONS = 0x00010112, ///< Optional protocol flags (encoded)
|
||||
RNDIS_OID_GEN_MAC_OPTIONS = 0x00010113, ///< Optional NIC flags (encoded)
|
||||
RNDIS_OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, ///< Whether the NIC is connected to the network
|
||||
RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, ///< The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction
|
||||
|
||||
//------------- General Optional OIDs -------------//
|
||||
RNDIS_OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, ///< Vendor-assigned version number of the driver
|
||||
RNDIS_OID_GEN_SUPPORTED_GUIDS = 0x00010117, ///< The custom GUIDs (Globally Unique Identifier) supported by the miniport driver
|
||||
RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, ///< List of network-layer addresses associated with the binding between a transport and the driver
|
||||
RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, ///< Size of packets' additional headers
|
||||
RNDIS_OID_GEN_MEDIA_CAPABILITIES = 0x00010201, ///<
|
||||
RNDIS_OID_GEN_PHYSICAL_MEDIUM = 0x00010202, ///< Physical media supported by the miniport driver (encoded)
|
||||
|
||||
//------------- 802.3 Objects (Ethernet) -------------//
|
||||
RNDIS_OID_802_3_PERMANENT_ADDRESS = 0x01010101, ///< Permanent station address
|
||||
RNDIS_OID_802_3_CURRENT_ADDRESS = 0x01010102, ///< Current station address
|
||||
RNDIS_OID_802_3_MULTICAST_LIST = 0x01010103, ///< Current multicast address list
|
||||
RNDIS_OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, ///< Maximum size of multicast address list
|
||||
} rndis_oid_type_t;
|
||||
|
||||
/// RNDIS Packet Filter Bits \ref RNDIS_OID_GEN_CURRENT_PACKET_FILTER.
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_PACKET_TYPE_DIRECTED = 0x00000001, ///< Directed packets. Directed packets contain a destination address equal to the station address of the NIC.
|
||||
RNDIS_PACKET_TYPE_MULTICAST = 0x00000002, ///< Multicast address packets sent to addresses in the multicast address list.
|
||||
RNDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004, ///< All multicast address packets, not just the ones enumerated in the multicast address list.
|
||||
RNDIS_PACKET_TYPE_BROADCAST = 0x00000008, ///< Broadcast packets.
|
||||
RNDIS_PACKET_TYPE_SOURCE_ROUTING = 0x00000010, ///< All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge.
|
||||
RNDIS_PACKET_TYPE_PROMISCUOUS = 0x00000020, ///< Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not.
|
||||
RNDIS_PACKET_TYPE_SMT = 0x00000040, ///< SMT packets that an FDDI NIC receives.
|
||||
RNDIS_PACKET_TYPE_ALL_LOCAL = 0x00000080, ///< All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle.
|
||||
RNDIS_PACKET_TYPE_GROUP = 0x00001000, ///< Packets sent to the current group address.
|
||||
RNDIS_PACKET_TYPE_ALL_FUNCTIONAL = 0x00002000, ///< All functional address packets, not just the ones in the current functional address.
|
||||
RNDIS_PACKET_TYPE_FUNCTIONAL = 0x00004000, ///< Functional address packets sent to addresses included in the current functional address.
|
||||
RNDIS_PACKET_TYPE_MAC_FRAME = 0x00008000, ///< NIC driver frames that a Token Ring NIC receives.
|
||||
RNDIS_PACKET_TYPE_NO_LOCAL = 0x00010000,
|
||||
} rndis_packet_filter_type_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_RNDIS_H_ */
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_common.h"
|
||||
#include "cdc_host.h"
|
||||
#include "cdc_rndis_host.h"
|
||||
|
||||
#if 0 // TODO remove subtask related macros later
|
||||
// Sub Task
|
||||
#define OSAL_SUBTASK_BEGIN
|
||||
#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
|
||||
|
||||
#define STASK_RETURN(_error) return _error;
|
||||
#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
|
||||
#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
|
||||
|
||||
CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
|
||||
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
|
||||
|
||||
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
// TODO Microsoft requires message length for any get command must be at least 4096 bytes
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_error_t rndis_body_subtask(void);
|
||||
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
|
||||
uint8_t * p_mess, uint32_t mess_length,
|
||||
uint8_t *p_response );
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
|
||||
{
|
||||
TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
|
||||
TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// IMPLEMENTATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
|
||||
// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
|
||||
// forever loop cannot have any return at all.
|
||||
OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
|
||||
{
|
||||
OSAL_TASK_BEGIN
|
||||
rndis_body_subtask();
|
||||
OSAL_TASK_END
|
||||
}
|
||||
|
||||
static tusb_error_t rndis_body_subtask(void)
|
||||
{
|
||||
static uint8_t relative_addr;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tusb_time_delay_ms_api(100);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// RNDIS-CDC Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void rndish_init(void)
|
||||
{
|
||||
tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
|
||||
|
||||
//------------- Task creation -------------//
|
||||
|
||||
//------------- semaphore creation for notification pipe -------------//
|
||||
for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
|
||||
{
|
||||
rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
|
||||
}
|
||||
}
|
||||
|
||||
void rndish_close(uint8_t dev_addr)
|
||||
{
|
||||
osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
|
||||
// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
|
||||
}
|
||||
|
||||
|
||||
static rndis_msg_initialize_t const msg_init =
|
||||
{
|
||||
.type = RNDIS_MSG_INITIALIZE,
|
||||
.length = sizeof(rndis_msg_initialize_t),
|
||||
.request_id = 1, // TODO should use some magic number
|
||||
.major_version = 1,
|
||||
.minor_version = 0,
|
||||
.max_xfer_size = 0x4000 // TODO mimic windows
|
||||
};
|
||||
|
||||
static rndis_msg_query_t const msg_query_permanent_addr =
|
||||
{
|
||||
.type = RNDIS_MSG_QUERY,
|
||||
.length = sizeof(rndis_msg_query_t)+6,
|
||||
.request_id = 1,
|
||||
.oid = RNDIS_OID_802_3_PERMANENT_ADDRESS,
|
||||
.buffer_length = 6,
|
||||
.buffer_offset = 20,
|
||||
};
|
||||
|
||||
static rndis_msg_set_t const msg_set_packet_filter =
|
||||
{
|
||||
.type = RNDIS_MSG_SET,
|
||||
.length = sizeof(rndis_msg_set_t)+4,
|
||||
.request_id = 1,
|
||||
.oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
|
||||
.buffer_length = 4,
|
||||
.buffer_offset = 20,
|
||||
};
|
||||
|
||||
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
|
||||
{
|
||||
tusb_error_t error;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
//------------- Message Initialize -------------//
|
||||
memcpy(msg_payload, &msg_init, sizeof(rndis_msg_initialize_t));
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_initialize_t),
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
// TODO currently not support multiple data packets per xfer
|
||||
rndis_msg_initialize_cmplt_t * const p_init_cmpt = (rndis_msg_initialize_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
|
||||
p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
|
||||
rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
|
||||
|
||||
//------------- Message Query 802.3 Permanent Address -------------//
|
||||
memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t));
|
||||
tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
|
||||
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_query_t) + 6,
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
|
||||
memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
|
||||
|
||||
//------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
|
||||
memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t));
|
||||
tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
|
||||
((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST);
|
||||
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_set_t) + 4,
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
|
||||
|
||||
tusbh_cdc_rndis_mounted_cb(dev_addr);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
|
||||
{
|
||||
if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
|
||||
{
|
||||
osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL & HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
|
||||
uint8_t * p_mess, uint32_t mess_length,
|
||||
uint8_t *p_response)
|
||||
{
|
||||
tusb_error_t error;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
//------------- Send RNDIS Control Message -------------//
|
||||
STASK_INVOKE(
|
||||
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
|
||||
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
|
||||
mess_length, p_mess),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
//------------- waiting for Response Available notification -------------//
|
||||
(void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8);
|
||||
osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
|
||||
|
||||
//------------- Get RNDIS Message Initialize Complete -------------//
|
||||
STASK_INVOKE(
|
||||
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
|
||||
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
|
||||
RNDIS_MSG_PAYLOAD_MAX, p_response),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
|
||||
//{
|
||||
// tusb_error_t error;
|
||||
//
|
||||
// OSAL_SUBTASK_BEGIN
|
||||
//
|
||||
// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
|
||||
// {
|
||||
// .type = RNDIS_MSG_INITIALIZE,
|
||||
// .length = sizeof(rndis_msg_initialize_t),
|
||||
// .request_id = 1, // TODO should use some magic number
|
||||
// .major_version = 1,
|
||||
// .minor_version = 0,
|
||||
// .max_xfer_size = 0x4000 // TODO mimic windows
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
// OSAL_SUBTASK_END
|
||||
//}
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup CDC_RNDIS
|
||||
* \defgroup CDC_RNSID_Host Host
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CDC_RNDIS_HOST_H_
|
||||
#define _TUSB_CDC_RNDIS_HOST_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "host/usbh.h"
|
||||
#include "cdc_rndis.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL RNDIS-CDC Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
OSAL_SEM_DEF(semaphore_notification);
|
||||
osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe
|
||||
uint32_t max_xfer_size; // got from device's msg initialize complete
|
||||
uint8_t mac_address[6];
|
||||
}rndish_data_t;
|
||||
|
||||
void rndish_init(void);
|
||||
bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
|
||||
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void rndish_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_RNDIS_HOST_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Heiko Kuester
|
||||
*
|
||||
* 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_CH34X_H
|
||||
#define TUSB_CH34X_H
|
||||
|
||||
// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
|
||||
// - https://github.com/WCHSoftGroup/ch341ser_linux
|
||||
// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
|
||||
// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
|
||||
|
||||
// set line_coding @ enumeration
|
||||
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
#else // this default is necessary to work properly
|
||||
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
|
||||
#endif
|
||||
|
||||
// USB requests
|
||||
#define CH34X_REQ_READ_VERSION 0x5F // dec 95
|
||||
#define CH34X_REQ_WRITE_REG 0x9A // dec 154
|
||||
#define CH34X_REQ_READ_REG 0x95 // dec 149
|
||||
#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161
|
||||
#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164
|
||||
|
||||
// registers
|
||||
#define CH34X_REG_BREAK 0x05
|
||||
#define CH34X_REG_PRESCALER 0x12
|
||||
#define CH34X_REG_DIVISOR 0x13
|
||||
#define CH34X_REG_LCR 0x18
|
||||
#define CH34X_REG_LCR2 0x25
|
||||
#define CH34X_REG_MCR_MSR 0x06
|
||||
#define CH34X_REG_MCR_MSR2 0x07
|
||||
#define CH34X_NBREAK_BITS 0x01
|
||||
|
||||
#define CH341_REG_0x0F 0x0F // undocumented register
|
||||
#define CH341_REG_0x2C 0x2C // undocumented register
|
||||
#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
|
||||
|
||||
#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
|
||||
#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
|
||||
|
||||
// modem control bits
|
||||
#define CH34X_BIT_RTS (1 << 6)
|
||||
#define CH34X_BIT_DTR (1 << 5)
|
||||
|
||||
// line control bits
|
||||
#define CH34X_LCR_ENABLE_RX 0x80
|
||||
#define CH34X_LCR_ENABLE_TX 0x40
|
||||
#define CH34X_LCR_MARK_SPACE 0x20
|
||||
#define CH34X_LCR_PAR_EVEN 0x10
|
||||
#define CH34X_LCR_ENABLE_PAR 0x08
|
||||
#define CH34X_LCR_PAR_MASK 0x38 // all parity bits
|
||||
#define CH34X_LCR_STOP_BITS_2 0x04
|
||||
#define CH34X_LCR_CS8 0x03
|
||||
#define CH34X_LCR_CS7 0x02
|
||||
#define CH34X_LCR_CS6 0x01
|
||||
#define CH34X_LCR_CS5 0x00
|
||||
#define CH34X_LCR_CS_MASK 0x03 // all CSx bits
|
||||
|
||||
#endif // TUSB_CH34X_H
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_CP210X_H
|
||||
#define TUSB_CP210X_H
|
||||
|
||||
// Protocol details can be found at AN571: CP210x Virtual COM Port Interface
|
||||
// https://www.silabs.com/documents/public/application-notes/AN571.pdf
|
||||
|
||||
// parts are overtaken from vendors driver
|
||||
// https://www.silabs.com/documents/public/software/cp210x-3.1.0.tar.gz
|
||||
|
||||
// Config request codes
|
||||
#define CP210X_IFC_ENABLE 0x00
|
||||
#define CP210X_SET_BAUDDIV 0x01
|
||||
#define CP210X_GET_BAUDDIV 0x02
|
||||
#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
|
||||
#define CP210X_GET_LINE_CTL 0x04
|
||||
#define CP210X_SET_BREAK 0x05
|
||||
#define CP210X_IMM_CHAR 0x06
|
||||
#define CP210X_SET_MHS 0x07 // Set DTR, RTS
|
||||
#define CP210X_GET_MDMSTS 0x08 // Get modem status (DTR, RTS, CTS, DSR, RI, DCD)
|
||||
#define CP210X_SET_XON 0x09
|
||||
#define CP210X_SET_XOFF 0x0A
|
||||
#define CP210X_SET_EVENTMASK 0x0B
|
||||
#define CP210X_GET_EVENTMASK 0x0C
|
||||
#define CP210X_SET_CHAR 0x0D
|
||||
#define CP210X_GET_CHARS 0x0E
|
||||
#define CP210X_GET_PROPS 0x0F
|
||||
#define CP210X_GET_COMM_STATUS 0x10
|
||||
#define CP210X_RESET 0x11
|
||||
#define CP210X_PURGE 0x12
|
||||
#define CP210X_SET_FLOW 0x13
|
||||
#define CP210X_GET_FLOW 0x14
|
||||
#define CP210X_EMBED_EVENTS 0x15
|
||||
#define CP210X_GET_EVENTSTATE 0x16
|
||||
#define CP210X_SET_CHARS 0x19
|
||||
#define CP210X_GET_BAUDRATE 0x1D
|
||||
#define CP210X_SET_BAUDRATE 0x1E
|
||||
#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device
|
||||
|
||||
// SILABSER_IFC_ENABLE_REQUEST_CODE
|
||||
#define CP210X_UART_ENABLE 0x0001
|
||||
#define CP210X_UART_DISABLE 0x0000
|
||||
|
||||
// SILABSER_SET_BAUDDIV_REQUEST_CODE
|
||||
#define CP210X_BAUD_RATE_GEN_FREQ 0x384000
|
||||
|
||||
// SILABSER_SET_LINE_CTL_REQUEST_CODE
|
||||
#define CP210X_BITS_DATA_MASK 0x0f00
|
||||
#define CP210X_BITS_DATA_5 0x0500
|
||||
#define CP210X_BITS_DATA_6 0x0600
|
||||
#define CP210X_BITS_DATA_7 0x0700
|
||||
#define CP210X_BITS_DATA_8 0x0800
|
||||
#define CP210X_BITS_DATA_9 0x0900
|
||||
|
||||
#define CP210X_BITS_PARITY_MASK 0x00f0
|
||||
#define CP210X_BITS_PARITY_NONE 0x0000
|
||||
#define CP210X_BITS_PARITY_ODD 0x0010
|
||||
#define CP210X_BITS_PARITY_EVEN 0x0020
|
||||
#define CP210X_BITS_PARITY_MARK 0x0030
|
||||
#define CP210X_BITS_PARITY_SPACE 0x0040
|
||||
|
||||
#define CP210X_BITS_STOP_MASK 0x000f
|
||||
#define CP210X_BITS_STOP_1 0x0000
|
||||
#define CP210X_BITS_STOP_1_5 0x0001
|
||||
#define CP210X_BITS_STOP_2 0x0002
|
||||
|
||||
// SILABSER_SET_BREAK_REQUEST_CODE
|
||||
#define CP210X_BREAK_ON 0x0001
|
||||
#define CP210X_BREAK_OFF 0x0000
|
||||
|
||||
// SILABSER_SET_MHS_REQUEST_CODE
|
||||
#define CP210X_MCR_DTR 0x0001
|
||||
#define CP210X_MCR_RTS 0x0002
|
||||
#define CP210X_MCR_ALL 0x0003
|
||||
#define CP210X_MSR_CTS 0x0010
|
||||
#define CP210X_MSR_DSR 0x0020
|
||||
#define CP210X_MSR_RING 0x0040
|
||||
#define CP210X_MSR_DCD 0x0080
|
||||
#define CP210X_MSR_ALL 0x00F0
|
||||
|
||||
#define CP210X_CONTROL_WRITE_DTR 0x0100UL
|
||||
#define CP210X_CONTROL_WRITE_RTS 0x0200UL
|
||||
|
||||
#define CP210X_LSR_BREAK 0x0001
|
||||
#define CP210X_LSR_FRAMING_ERROR 0x0002
|
||||
#define CP210X_LSR_HW_OVERRUN 0x0004
|
||||
#define CP210X_LSR_QUEUE_OVERRUN 0x0008
|
||||
#define CP210X_LSR_PARITY_ERROR 0x0010
|
||||
#define CP210X_LSR_ALL 0x001F
|
||||
|
||||
#endif //TUSB_CP210X_H
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_FTDI_SIO_H
|
||||
#define TUSB_FTDI_SIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Commands
|
||||
#define FTDI_SIO_RESET 0 // Reset the port
|
||||
#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register
|
||||
#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register
|
||||
#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate
|
||||
#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port
|
||||
#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register
|
||||
#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character
|
||||
#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character
|
||||
#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer
|
||||
#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer
|
||||
#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode
|
||||
#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins
|
||||
#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM
|
||||
|
||||
// Channel indices for FT2232, FT2232H and FT4232H devices
|
||||
#define CHANNEL_A 1
|
||||
#define CHANNEL_B 2
|
||||
#define CHANNEL_C 3
|
||||
#define CHANNEL_D 4
|
||||
|
||||
// Port Identifier Table
|
||||
#define PIT_DEFAULT 0 // SIOA
|
||||
#define PIT_SIOA 1 // SIOA
|
||||
// The device this driver is tested with one has only one port
|
||||
#define PIT_SIOB 2 // SIOB
|
||||
#define PIT_PARALLEL 3 // Parallel
|
||||
|
||||
// FTDI_SIO_RESET
|
||||
#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET
|
||||
#define FTDI_SIO_RESET_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_RESET_SIO 0
|
||||
#define FTDI_SIO_RESET_PURGE_RX 1
|
||||
#define FTDI_SIO_RESET_PURGE_TX 2
|
||||
|
||||
// FTDI_SIO_SET_BAUDRATE
|
||||
#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
|
||||
|
||||
enum ftdi_sio_baudrate {
|
||||
ftdi_sio_b300 = 0,
|
||||
ftdi_sio_b600 = 1,
|
||||
ftdi_sio_b1200 = 2,
|
||||
ftdi_sio_b2400 = 3,
|
||||
ftdi_sio_b4800 = 4,
|
||||
ftdi_sio_b9600 = 5,
|
||||
ftdi_sio_b19200 = 6,
|
||||
ftdi_sio_b38400 = 7,
|
||||
ftdi_sio_b57600 = 8,
|
||||
ftdi_sio_b115200 = 9
|
||||
};
|
||||
|
||||
// FTDI_SIO_SET_DATA
|
||||
#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA
|
||||
#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
|
||||
#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
|
||||
#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
|
||||
#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
|
||||
#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
|
||||
#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) // same coding as ACM
|
||||
#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) // 1.5 not supported, for future use?
|
||||
#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
|
||||
#define FTDI_SIO_SET_BREAK (0x1 << 14)
|
||||
|
||||
// FTDI_SIO_MODEM_CTRL
|
||||
#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL
|
||||
|
||||
#define FTDI_SIO_SET_DTR_MASK 0x1UL
|
||||
#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1UL)
|
||||
#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0UL)
|
||||
#define FTDI_SIO_SET_RTS_MASK 0x2UL
|
||||
#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2UL)
|
||||
#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0UL)
|
||||
|
||||
// FTDI_SIO_SET_FLOW_CTRL
|
||||
#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL
|
||||
#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
|
||||
#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
|
||||
#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
|
||||
#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
|
||||
|
||||
// FTDI_SIO_GET_LATENCY_TIMER
|
||||
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER
|
||||
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0
|
||||
|
||||
// FTDI_SIO_SET_LATENCY_TIMER
|
||||
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
|
||||
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
|
||||
|
||||
// FTDI_SIO_SET_EVENT_CHAR
|
||||
#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR
|
||||
#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40
|
||||
|
||||
// FTDI_SIO_GET_MODEM_STATUS
|
||||
#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0
|
||||
#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS
|
||||
#define FTDI_SIO_CTS_MASK 0x10
|
||||
#define FTDI_SIO_DSR_MASK 0x20
|
||||
#define FTDI_SIO_RI_MASK 0x40
|
||||
#define FTDI_SIO_RLSD_MASK 0x80
|
||||
|
||||
// FTDI_SIO_SET_BITMODE
|
||||
#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE
|
||||
|
||||
// Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST
|
||||
#define FTDI_SIO_BITMODE_RESET 0x00
|
||||
#define FTDI_SIO_BITMODE_CBUS 0x20
|
||||
|
||||
// FTDI_SIO_READ_PINS
|
||||
#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0
|
||||
#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS
|
||||
|
||||
// FTDI_SIO_READ_EEPROM
|
||||
#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
|
||||
#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
|
||||
|
||||
#define FTDI_FTX_CBUS_MUX_GPIO 0x8
|
||||
#define FTDI_FT232R_CBUS_MUX_GPIO 0xa
|
||||
|
||||
#define FTDI_RS0_CTS (1 << 4)
|
||||
#define FTDI_RS0_DSR (1 << 5)
|
||||
#define FTDI_RS0_RI (1 << 6)
|
||||
#define FTDI_RS0_RLSD (1 << 7)
|
||||
|
||||
#define FTDI_RS_DR 1
|
||||
#define FTDI_RS_OE (1 << 1)
|
||||
#define FTDI_RS_PE (1 << 2)
|
||||
#define FTDI_RS_FE (1 << 3)
|
||||
#define FTDI_RS_BI (1 << 4)
|
||||
#define FTDI_RS_THRE (1 << 5)
|
||||
#define FTDI_RS_TEMT (1 << 6)
|
||||
#define FTDI_RS_FIFO (1 << 7)
|
||||
|
||||
// chip types and names
|
||||
typedef enum ftdi_chip_type {
|
||||
FTDI_SIO = 0,
|
||||
// FTDI_FT232A,
|
||||
FTDI_FT232B,
|
||||
FTDI_FT2232C,
|
||||
FTDI_FT232R,
|
||||
FTDI_FT232H,
|
||||
FTDI_FT2232H,
|
||||
FTDI_FT4232H,
|
||||
FTDI_FT4232HA,
|
||||
FTDI_FT232HP,
|
||||
FTDI_FT233HP,
|
||||
FTDI_FT2232HP,
|
||||
FTDI_FT2233HP,
|
||||
FTDI_FT4232HP,
|
||||
FTDI_FT4233HP,
|
||||
FTDI_FTX,
|
||||
FTDI_UNKNOWN
|
||||
} ftdi_chip_type_t;
|
||||
|
||||
#define FTDI_CHIP_NAMES \
|
||||
[FTDI_SIO] = "SIO", /* the serial part of FT8U100AX */ \
|
||||
/* [FTDI_FT232A] = "FT232A", */ \
|
||||
[FTDI_FT232B] = "FT232B", \
|
||||
[FTDI_FT2232C] = "FT2232C/D", \
|
||||
[FTDI_FT232R] = "FT232R", \
|
||||
[FTDI_FT232H] = "FT232H", \
|
||||
[FTDI_FT2232H] = "FTDI_FT2232H", \
|
||||
[FTDI_FT4232H] = "FT4232H", \
|
||||
[FTDI_FT4232HA] = "FT4232HA", \
|
||||
[FTDI_FT232HP] = "FT232HP", \
|
||||
[FTDI_FT233HP] = "FT233HP", \
|
||||
[FTDI_FT2232HP] = "FT2232HP", \
|
||||
[FTDI_FT2233HP] = "FT2233HP", \
|
||||
[FTDI_FT4232HP] = "FT4232HP", \
|
||||
[FTDI_FT4233HP] = "FT4233HP", \
|
||||
[FTDI_FTX] = "FT-X", \
|
||||
[FTDI_UNKNOWN] = "UNKNOWN"
|
||||
|
||||
// private interface data
|
||||
typedef struct ftdi_private {
|
||||
ftdi_chip_type_t chip_type;
|
||||
uint8_t channel; // channel index, or 0 for legacy types
|
||||
} ftdi_private_t;
|
||||
|
||||
#define FTDI_OK true
|
||||
#define FTDI_FAIL false
|
||||
#define FTDI_NOT_POSSIBLE -1
|
||||
#define FTDI_REQUESTED -2
|
||||
|
||||
// division and round function overtaken from math.h
|
||||
#define DIV_ROUND_CLOSEST(x, divisor)( \
|
||||
{ \
|
||||
typeof(x) __x = x; \
|
||||
typeof(divisor) __d = divisor; \
|
||||
(((typeof(x))-1) > 0 || \
|
||||
((typeof(divisor))-1) > 0 || \
|
||||
(((__x) > 0) == ((__d) > 0))) ? \
|
||||
(((__x) + ((__d) / 2)) / (__d)) : \
|
||||
(((__x) - ((__d) / 2)) / (__d)); \
|
||||
} \
|
||||
)
|
||||
|
||||
#endif //TUSB_FTDI_SIO_H
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Heiko Kuester
|
||||
*
|
||||
* 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_PL2303_H
|
||||
#define TUSB_PL2303_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// There is no official documentation for the PL2303 chips.
|
||||
// Reference can be found
|
||||
// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.h and
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c
|
||||
// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uplcom.c
|
||||
|
||||
// quirks
|
||||
#define PL2303_QUIRK_UART_STATE_IDX0 1
|
||||
#define PL2303_QUIRK_LEGACY 2
|
||||
#define PL2303_QUIRK_ENDPOINT_HACK 4
|
||||
|
||||
// requests and bits
|
||||
#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface
|
||||
#define PL2303_SET_LINE_REQUEST 0x20 // dec 32
|
||||
|
||||
#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface
|
||||
#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34
|
||||
#define PL2303_CONTROL_DTR 0x01 // dec 1
|
||||
#define PL2303_CONTROL_RTS 0x02 // dec 2
|
||||
|
||||
#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface
|
||||
#define PL2303_BREAK_REQUEST 0x23 // dec 35
|
||||
#define PL2303_BREAK_ON 0xffff
|
||||
#define PL2303_BREAK_OFF 0x0000
|
||||
|
||||
#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface
|
||||
#define PL2303_GET_LINE_REQUEST 0x21 // dec 33
|
||||
|
||||
#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface
|
||||
#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1
|
||||
#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128
|
||||
|
||||
#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface
|
||||
#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1
|
||||
#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129
|
||||
|
||||
#define PL2303_UART_STATE_INDEX 8
|
||||
#define PL2303_UART_STATE_MSR_MASK 0x8b
|
||||
#define PL2303_UART_STATE_TRANSIENT_MASK 0x74
|
||||
#define PL2303_UART_DCD 0x01
|
||||
#define PL2303_UART_DSR 0x02
|
||||
#define PL2303_UART_BREAK_ERROR 0x04
|
||||
#define PL2303_UART_RING 0x08
|
||||
#define PL2303_UART_FRAME_ERROR 0x10
|
||||
#define PL2303_UART_PARITY_ERROR 0x20
|
||||
#define PL2303_UART_OVERRUN_ERROR 0x40
|
||||
#define PL2303_UART_CTS 0x80
|
||||
|
||||
#define PL2303_FLOWCTRL_MASK 0xf0
|
||||
|
||||
#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint
|
||||
|
||||
// registers via vendor read/write requests
|
||||
#define PL2303_READ_TYPE_HX_STATUS 0x8080
|
||||
|
||||
#define PL2303_HXN_RESET_REG 0x07
|
||||
#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02
|
||||
#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01
|
||||
|
||||
#define PL2303_HXN_FLOWCTRL_REG 0x0a
|
||||
#define PL2303_HXN_FLOWCTRL_MASK 0x1c
|
||||
#define PL2303_HXN_FLOWCTRL_NONE 0x1c
|
||||
#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
|
||||
#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
|
||||
|
||||
// type data
|
||||
typedef enum pl2303_type {
|
||||
PL2303_TYPE_H = 0, // 0
|
||||
PL2303_TYPE_HX, // 1
|
||||
PL2303_TYPE_TA, // 2
|
||||
PL2303_TYPE_TB, // 3
|
||||
PL2303_TYPE_HXD, // 4
|
||||
PL2303_TYPE_HXN, // 5
|
||||
PL2303_TYPE_COUNT,
|
||||
PL2303_TYPE_NEED_SUPPORTS_HX_STATUS,
|
||||
PL2303_TYPE_UNKNOWN,
|
||||
} pl2303_type_t;
|
||||
|
||||
typedef struct pl2303_type_data {
|
||||
uint32_t max_baud_rate;
|
||||
uint8_t quirks;
|
||||
uint8_t no_autoxonxoff : 1;
|
||||
uint8_t no_divisors : 1;
|
||||
uint8_t alt_divisors : 1;
|
||||
} pl2303_type_data_t;
|
||||
|
||||
#define PL2303_TYPE_DATA \
|
||||
[PL2303_TYPE_H] = { \
|
||||
.max_baud_rate = 1228800, .quirks = PL2303_QUIRK_LEGACY, \
|
||||
.no_autoxonxoff = 1, .no_divisors = 0, .alt_divisors = 0 \
|
||||
}, \
|
||||
[PL2303_TYPE_HX] = { \
|
||||
.max_baud_rate = 6000000, .quirks = 0, \
|
||||
.no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 0 \
|
||||
}, \
|
||||
[PL2303_TYPE_TA] = { \
|
||||
.max_baud_rate = 6000000, .quirks = 0, \
|
||||
.no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 1 \
|
||||
}, \
|
||||
[PL2303_TYPE_TB] = { \
|
||||
.max_baud_rate = 12000000, .quirks = 0, \
|
||||
.no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 1 \
|
||||
}, \
|
||||
[PL2303_TYPE_HXD] = { \
|
||||
.max_baud_rate = 12000000, .quirks = 0, \
|
||||
.no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 0 \
|
||||
}, \
|
||||
[PL2303_TYPE_HXN] = { \
|
||||
.max_baud_rate = 12000000, .quirks = 0, \
|
||||
.no_autoxonxoff = 0, .no_divisors = 1, .alt_divisors = 0 \
|
||||
}
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
pl2303_type_t type;
|
||||
uint8_t quirks;
|
||||
bool supports_hx_status;
|
||||
} pl2303_private_t;
|
||||
|
||||
// buffer sizes for line coding data
|
||||
#define PL2303_LINE_CODING_BUFSIZE 7
|
||||
#define PL2303_LINE_CODING_BAUDRATE_BUFSIZE 4
|
||||
|
||||
// bulk endpoints
|
||||
#define PL2303_OUT_EP 0x02
|
||||
#define PL2303_IN_EP 0x83
|
||||
|
||||
#endif // TUSB_PL2303_H
|
||||
119
managed_components/espressif__tinyusb/src/class/dfu/dfu.h
Normal file
119
managed_components/espressif__tinyusb/src/class/dfu/dfu.h
Normal file
@@ -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_ */
|
||||
430
managed_components/espressif__tinyusb/src/class/dfu/dfu_device.c
Normal file
430
managed_components/espressif__tinyusb/src/class/dfu/dfu_device.c
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* 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 (CFG_TUD_ENABLED && CFG_TUD_DFU)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "dfu_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_DFU_LOG_LEVEL
|
||||
#define CFG_TUD_DFU_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_DFU_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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;
|
||||
} dfu_state_ctx_t;
|
||||
|
||||
// Only a single dfu state is allowed
|
||||
static dfu_state_ctx_t _dfu_ctx;
|
||||
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(transfer_buf, CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
} _dfu_epbuf;
|
||||
|
||||
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, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
|
||||
static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
|
||||
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_dfu_detach_cb(void) {
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt) {
|
||||
(void) alt;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length) {
|
||||
(void) alt;
|
||||
(void) block_num;
|
||||
(void) data;
|
||||
(void) length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
|
||||
tu_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" },
|
||||
};
|
||||
|
||||
tu_static tu_lookup_table_t const _dfu_request_table = {
|
||||
.count = TU_ARRAY_SIZE(_dfu_request_lookup),
|
||||
.items = _dfu_request_lookup
|
||||
};
|
||||
|
||||
tu_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" },
|
||||
};
|
||||
|
||||
tu_static tu_lookup_table_t const _dfu_state_table = {
|
||||
.count = TU_ARRAY_SIZE(_dfu_state_lookup),
|
||||
.items = _dfu_state_lookup
|
||||
};
|
||||
|
||||
tu_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" },
|
||||
};
|
||||
|
||||
tu_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);
|
||||
}
|
||||
|
||||
bool dfu_moded_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t dfu_moded_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
|
||||
//------------- Interface (with Alt) descriptor -------------//
|
||||
const uint8_t itf_num = itf_desc->bInterfaceNumber;
|
||||
uint8_t alt_count = 0;
|
||||
|
||||
uint16_t drv_len = 0;
|
||||
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 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 = (const tusb_desc_interface_t*) tu_desc_next(itf_desc);
|
||||
}
|
||||
|
||||
//------------- DFU Functional descriptor -------------//
|
||||
const tusb_desc_dfu_functional_t*func_desc = (const tusb_desc_dfu_functional_t*) 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
|
||||
const uint16_t transfer_size = tu_le16toh( tu_unaligned_read16((const 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, const tusb_control_request_t* request) {
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
TU_LOG_DRV(" 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_LOG_DRV(" 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) {
|
||||
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) {
|
||||
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(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
|
||||
const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
|
||||
request->wLength);
|
||||
|
||||
return tud_control_xfer(rhport, request, _dfu_epbuf.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_epbuf.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, const tusb_control_request_t* 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_epbuf.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, const tusb_control_request_t* 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, const tusb_control_request_t* 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
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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
|
||||
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
|
||||
void tud_dfu_detach_cb(void);
|
||||
|
||||
// Invoked when the Host has terminated a download or upload transfer
|
||||
void tud_dfu_abort_cb(uint8_t alt);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_moded_init(void);
|
||||
bool dfu_moded_deinit(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_ */
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_DFU_RUNTIME)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "dfu_rt_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_DFU_RUNTIME_LOG_LEVEL
|
||||
#define CFG_TUD_DFU_RUNTIME_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_DFU_RUNTIME_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_rtd_init(void) {
|
||||
}
|
||||
|
||||
bool dfu_rtd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void dfu_rtd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
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_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 );
|
||||
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||
|
||||
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
|
||||
{
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
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_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
// nothing to do with DATA or ACK stage
|
||||
if ( stage != CONTROL_STAGE_SETUP ) return true;
|
||||
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
|
||||
// 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);
|
||||
|
||||
switch (request->bRequest)
|
||||
{
|
||||
case DFU_REQUEST_DETACH:
|
||||
{
|
||||
TU_LOG_DRV(" DFU RT Request: DETACH\r\n");
|
||||
tud_control_status(rhport, request);
|
||||
tud_dfu_runtime_reboot_to_dfu_cb();
|
||||
}
|
||||
break;
|
||||
|
||||
case DFU_REQUEST_GETSTATUS:
|
||||
{
|
||||
TU_LOG_DRV(" DFU RT Request: GETSTATUS\r\n");
|
||||
dfu_status_response_t resp;
|
||||
// Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
|
||||
TU_VERIFY(tu_memset_s(&resp, sizeof(resp), 0x00, sizeof(resp))==0);
|
||||
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
TU_LOG_DRV(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* 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_RT_DEVICE_H_
|
||||
#define _TUSB_DFU_RT_DEVICE_H_
|
||||
|
||||
#include "dfu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
// 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);
|
||||
bool dfu_rtd_deinit(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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_DFU_RT_DEVICE_H_ */
|
||||
2076
managed_components/espressif__tinyusb/src/class/hid/hid.h
Normal file
2076
managed_components/espressif__tinyusb/src/class/hid/hid.h
Normal file
File diff suppressed because it is too large
Load Diff
422
managed_components/espressif__tinyusb/src/class/hid/hid_device.c
Normal file
422
managed_components/espressif__tinyusb/src/class/hid/hid_device.c
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_HID)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "hid_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out; // optional Out endpoint
|
||||
uint8_t itf_protocol; // Boot mouse or keyboard
|
||||
|
||||
uint16_t report_desc_len;
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
uint8_t idle_rate; // up to application to handle idle rate
|
||||
|
||||
// TODO save hid descriptor since host can specifically request this after enumeration
|
||||
// Note: HID descriptor may be not available from application after enumeration
|
||||
const tusb_hid_descriptor_hid_t*hid_descriptor;
|
||||
} hidd_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(ctrl , CFG_TUD_HID_EP_BUFSIZE);
|
||||
TUD_EPBUF_DEF(epin , CFG_TUD_HID_EP_BUFSIZE);
|
||||
TUD_EPBUF_DEF(epout, CFG_TUD_HID_EP_BUFSIZE);
|
||||
} hidd_epbuf_t;
|
||||
|
||||
static hidd_interface_t _hidd_itf[CFG_TUD_HID];
|
||||
CFG_TUD_MEM_SECTION static hidd_epbuf_t _hidd_epbuf[CFG_TUD_HID];
|
||||
|
||||
/*------------- Helpers -------------*/
|
||||
TU_ATTR_ALWAYS_INLINE 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 i;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
|
||||
(void) instance;
|
||||
(void) protocol;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate) {
|
||||
(void) instance;
|
||||
(void) idle_rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) {
|
||||
(void) instance;
|
||||
(void) report;
|
||||
(void) len;
|
||||
}
|
||||
|
||||
// Invoked when a transfer wasn't successful
|
||||
TU_ATTR_WEAK void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes) {
|
||||
(void) instance;
|
||||
(void) report_type;
|
||||
(void) report;
|
||||
(void) xferred_bytes;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_hid_n_ready(uint8_t instance) {
|
||||
uint8_t const rhport = 0;
|
||||
uint8_t const ep_in = _hidd_itf[instance].ep_in;
|
||||
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
|
||||
}
|
||||
|
||||
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) {
|
||||
TU_VERIFY(instance < CFG_TUD_HID);
|
||||
const uint8_t rhport = 0;
|
||||
hidd_interface_t *p_hid = &_hidd_itf[instance];
|
||||
hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
|
||||
|
||||
// prepare data
|
||||
if (report_id) {
|
||||
p_epbuf->epin[0] = report_id;
|
||||
TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
|
||||
len++;
|
||||
} else {
|
||||
TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin, CFG_TUD_HID_EP_BUFSIZE, report, len));
|
||||
}
|
||||
|
||||
return usbd_edpt_xfer(rhport, p_hid->ep_in, p_epbuf->epin, len);
|
||||
}
|
||||
|
||||
uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
|
||||
return _hidd_itf[instance].itf_protocol;
|
||||
}
|
||||
|
||||
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, const uint8_t keycode[6]) {
|
||||
hid_keyboard_report_t report;
|
||||
report.modifier = modifier;
|
||||
report.reserved = 0;
|
||||
|
||||
if (keycode) {
|
||||
memcpy(report.keycode, keycode, sizeof(report.keycode));
|
||||
} else {
|
||||
tu_memclr(report.keycode, 6);
|
||||
}
|
||||
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
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 = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id,
|
||||
uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
|
||||
hid_abs_mouse_report_t report = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
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));
|
||||
}
|
||||
|
||||
bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
|
||||
hid_stylus_report_t report = {
|
||||
.attr = attrs,
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD-CLASS API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidd_init(void) {
|
||||
hidd_reset(0);
|
||||
}
|
||||
|
||||
bool hidd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidd_reset(uint8_t rhport) {
|
||||
(void)rhport;
|
||||
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
|
||||
}
|
||||
|
||||
uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(max_len >= drv_len, 0);
|
||||
|
||||
// Find available interface
|
||||
hidd_interface_t *p_hid;
|
||||
uint8_t hid_id;
|
||||
for (hid_id = 0; hid_id < CFG_TUD_HID; hid_id++) {
|
||||
p_hid = &_hidd_itf[hid_id];
|
||||
if (p_hid->ep_in == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(hid_id < CFG_TUD_HID, 0);
|
||||
hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_id];
|
||||
|
||||
uint8_t const *p_desc = (uint8_t const *)desc_itf;
|
||||
|
||||
//------------- HID descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
TU_ASSERT(HID_DESC_TYPE_HID == tu_desc_type(p_desc), 0);
|
||||
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *)p_desc;
|
||||
|
||||
//------------- 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), 0);
|
||||
|
||||
if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) {
|
||||
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
}
|
||||
|
||||
p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
// Use offsetof to avoid pointer to the odd/misaligned address
|
||||
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const *)p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
|
||||
|
||||
// Prepare for output endpoint
|
||||
if (p_hid->ep_out) {
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE), drv_len);
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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];
|
||||
hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_itf];
|
||||
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
|
||||
//------------- STD Request -------------//
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
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);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *)(uintptr_t)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 *)(uintptr_t)desc_report, p_hid->report_desc_len);
|
||||
} else {
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
}
|
||||
} else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
|
||||
//------------- Class Specific Request -------------//
|
||||
switch (request->bRequest) {
|
||||
case HID_REQ_CONTROL_GET_REPORT:
|
||||
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);
|
||||
|
||||
uint8_t* report_buf = p_epbuf->ctrl;
|
||||
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
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_epbuf->ctrl, xferlen);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_REPORT:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(request->wLength <= CFG_TUD_HID_EP_BUFSIZE);
|
||||
tud_control_xfer(rhport, request, p_epbuf->ctrl, 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_epbuf->ctrl;
|
||||
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
|
||||
// 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:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
p_hid->idle_rate = tu_u16_high(request->wValue);
|
||||
TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); // stall if false
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_GET_IDLE:
|
||||
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:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_PROTOCOL:
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
tud_control_status(rhport, request);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
p_hid->protocol_mode = (uint8_t) request->wValue;
|
||||
tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
} else {
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
uint8_t instance;
|
||||
hidd_interface_t *p_hid;
|
||||
|
||||
// Identify which interface to use
|
||||
for (instance = 0; instance < CFG_TUD_HID; instance++) {
|
||||
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);
|
||||
hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
|
||||
|
||||
if (ep_addr == p_hid->ep_in) {
|
||||
// Input report
|
||||
if (XFER_RESULT_SUCCESS == result) {
|
||||
tud_hid_report_complete_cb(instance, p_epbuf->epin, (uint16_t) xferred_bytes);
|
||||
} else {
|
||||
tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_epbuf->epin, (uint16_t) xferred_bytes);
|
||||
}
|
||||
} else {
|
||||
// Output report
|
||||
if (XFER_RESULT_SUCCESS == result) {
|
||||
tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t)xferred_bytes);
|
||||
} else {
|
||||
tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t) xferred_bytes);
|
||||
}
|
||||
|
||||
// prepare for new transfer
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
673
managed_components/espressif__tinyusb/src/class/hid/hid_device.h
Normal file
673
managed_components/espressif__tinyusb/src/class/hid/hid_device.h
Normal file
@@ -0,0 +1,673 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_HID_DEVICE_H_
|
||||
#define TUSB_HID_DEVICE_H_
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Default Configure & Validation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#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 (Multiple Instances) i.e. CFG_TUD_HID > 1
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if the interface is ready to use
|
||||
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);
|
||||
|
||||
// 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_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_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_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, const 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_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);
|
||||
|
||||
// ABSOLUTE MOUSE: convenient helper to send absolute mouse report if application
|
||||
// use template layout report as defined by hid_abs_mouse_report_t
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_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);
|
||||
|
||||
// STYLUS PEN: convenient helper to send absolute stylus pen report if application
|
||||
bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port)
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_ready(void) {
|
||||
return tud_hid_n_ready(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_interface_protocol(void) {
|
||||
return tud_hid_n_interface_protocol(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_get_protocol(void) {
|
||||
return tud_hid_n_get_protocol(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) {
|
||||
return tud_hid_n_report(0, report_id, report, len);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]) {
|
||||
return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE 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);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
|
||||
return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE 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);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_stylus_report(uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
|
||||
return tud_hid_n_stylus_report(0, report_id, attrs, x, y);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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(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 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 = OUTPUT)
|
||||
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
|
||||
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||
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).
|
||||
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
|
||||
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when a transfer wasn't successful
|
||||
void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes);
|
||||
|
||||
/* --------------------------------------------------------------------+
|
||||
* HID Report Descriptor Template
|
||||
*
|
||||
* Convenient for declaring popular HID device (keyboard, mouse, consumer,
|
||||
* 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
|
||||
* uint8_t const report_desc[] =
|
||||
* {
|
||||
* TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1) ) ,
|
||||
* TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2) )
|
||||
* };
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
// Keyboard Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_KEYBOARD(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
/* 8 bits Modifier Keys (Shift, Control, Alt) */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
|
||||
HID_USAGE_MIN ( 224 ) ,\
|
||||
HID_USAGE_MAX ( 231 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
HID_REPORT_COUNT ( 8 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 8 bit reserved */ \
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
/* Output 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_REPORT_COUNT ( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* led padding */ \
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_OUTPUT ( HID_CONSTANT ) ,\
|
||||
/* 6-byte Keycodes */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
|
||||
HID_USAGE_MIN ( 0 ) ,\
|
||||
HID_USAGE_MAX_N ( 255, 2 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX_N( 255, 2 ) ,\
|
||||
HID_REPORT_COUNT ( 6 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_MOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y position [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 2 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
/* Verital wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Stylus Pen Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \
|
||||
HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \
|
||||
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \
|
||||
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \
|
||||
HID_LOGICAL_MIN ( 0 ), \
|
||||
HID_LOGICAL_MAX ( 1 ), \
|
||||
HID_REPORT_SIZE ( 1 ), \
|
||||
HID_REPORT_COUNT( 2 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_REPORT_SIZE ( 1 ), \
|
||||
HID_REPORT_COUNT( 6 ), \
|
||||
HID_INPUT ( HID_CONSTANT | HID_ARRAY | HID_ABSOLUTE), \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), \
|
||||
HID_PHYSICAL_MAX_N( 0x7fff, 2 ), \
|
||||
HID_LOGICAL_MAX_N ( 0x7fff, 2 ), \
|
||||
HID_REPORT_SIZE ( 16 ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_UNIT_EXPONENT( 0x0f ), \
|
||||
HID_UNIT ( HID_VARIABLE | HID_NONLINEAR ), \
|
||||
HID_PHYSICAL_MIN( 0 ), \
|
||||
HID_PHYSICAL_MAX( 0 ), \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y absolute position [0, 32767] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_REPORT_COUNT ( 2 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Vertical wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Consumer Control Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_CONSUMER(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
|
||||
HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\
|
||||
HID_USAGE_MIN ( 0x00 ) ,\
|
||||
HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
/* System Control Report Descriptor Template
|
||||
* 0x00 - do nothing
|
||||
* 0x01 - Power Off
|
||||
* 0x02 - Standby
|
||||
* 0x03 - Wake Host
|
||||
*/
|
||||
#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
/* 2 bit system power control */ \
|
||||
HID_LOGICAL_MIN ( 1 ) ,\
|
||||
HID_LOGICAL_MAX ( 3 ) ,\
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 2 ) ,\
|
||||
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 */ \
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 6 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Gamepad Report Descriptor Template
|
||||
// with 32 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 (4 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 ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
/* 8 bit X, Y, Z, Rz, Rx, Ry (min -127, max 127 ) */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
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_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 ) ,\
|
||||
/* 32 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 \
|
||||
|
||||
// FIDO U2F Authenticator Descriptor Template
|
||||
// - 1st parameter is report size, which is 64 bytes maximum in U2F
|
||||
// - 2nd parameter is HID_REPORT_ID(n) (optional)
|
||||
#define TUD_HID_REPORT_DESC_FIDO_U2F(report_size, ...) \
|
||||
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_FIDO, 2 ) ,\
|
||||
HID_USAGE ( HID_USAGE_FIDO_U2FHID ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */ \
|
||||
__VA_ARGS__ \
|
||||
/* Usage Data In */ \
|
||||
HID_USAGE ( HID_USAGE_FIDO_DATA_IN ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_REPORT_COUNT ( report_size ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Usage Data Out */ \
|
||||
HID_USAGE ( HID_USAGE_FIDO_DATA_OUT ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_REPORT_COUNT ( report_size ) ,\
|
||||
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// HID Generic Input & Output
|
||||
// - 1st parameter is report size (mandatory)
|
||||
// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
|
||||
#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \
|
||||
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
|
||||
HID_USAGE ( 0x01 ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
/* Input */ \
|
||||
HID_USAGE ( 0x02 ),\
|
||||
HID_LOGICAL_MIN ( 0x00 ),\
|
||||
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT( report_size ),\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
/* Output */ \
|
||||
HID_USAGE ( 0x03 ),\
|
||||
HID_LOGICAL_MIN ( 0x00 ),\
|
||||
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT( report_size ),\
|
||||
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// HID Lighting and Illumination Report Descriptor Template
|
||||
// - 1st parameter is report id (required)
|
||||
// Creates 6 report ids for lighting HID usages in the following order:
|
||||
// report_id+0: HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT
|
||||
// report_id+1: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT
|
||||
// report_id+2: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT
|
||||
// report_id+3: HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT
|
||||
// report_id+4: HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT
|
||||
// report_id+5: HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT
|
||||
#define TUD_HID_REPORT_DESC_LIGHTING(report_id) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
|
||||
/* Lamp Array Attributes Report */ \
|
||||
HID_REPORT_ID (report_id ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_KIND ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
|
||||
HID_REPORT_SIZE ( 32 ),\
|
||||
HID_REPORT_COUNT ( 5 ),\
|
||||
HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Attributes Request Report */ \
|
||||
HID_REPORT_ID ( report_id + 1 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Attributes Response Report */ \
|
||||
HID_REPORT_ID ( report_id + 2 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_PURPOSES ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
|
||||
HID_REPORT_SIZE ( 32 ),\
|
||||
HID_REPORT_COUNT ( 5 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_IS_PROGRAMMABLE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INPUT_BINDING ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 6 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Multi-Update Report */ \
|
||||
HID_REPORT_ID ( report_id + 3 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 8 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 2 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 8 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 32 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Range Update Report */ \
|
||||
HID_REPORT_ID ( report_id + 4 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 8 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_START ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_END ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 2 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 4 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Array Control Report */ \
|
||||
HID_REPORT_ID ( report_id + 5 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_AUTONOMOUS_MODE ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 1 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidd_init (void);
|
||||
bool hidd_deinit (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
|
||||
804
managed_components/espressif__tinyusb/src/class/hid/hid_host.c
Normal file
804
managed_components/espressif__tinyusb/src/class/hid/hid_host.c
Normal file
@@ -0,0 +1,804 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_HID)
|
||||
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
|
||||
#include "hid_host.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUH_HID_LOG_LEVEL
|
||||
#define CFG_TUH_HID_LOG_LEVEL CFG_TUH_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_HID_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
bool mounted; // Enumeration is complete
|
||||
uint8_t itf_protocol; // None, Keyboard, Mouse
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
|
||||
uint8_t report_desc_type;
|
||||
uint16_t report_desc_len;
|
||||
|
||||
uint16_t epin_size;
|
||||
uint16_t epout_size;
|
||||
} hidh_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUH_EPBUF_DEF(epin, CFG_TUH_HID_EPIN_BUFSIZE);
|
||||
TUH_EPBUF_DEF(epout, CFG_TUH_HID_EPOUT_BUFSIZE);
|
||||
} hidh_epbuf_t;
|
||||
|
||||
static hidh_interface_t _hidh_itf[CFG_TUH_HID];
|
||||
CFG_TUH_MEM_SECTION static hidh_epbuf_t _hidh_epbuf[CFG_TUH_HID];
|
||||
|
||||
static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
(void) report_desc;
|
||||
(void) desc_len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
(void) report;
|
||||
(void) len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) len;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {
|
||||
(void) dev_addr;
|
||||
(void) idx;
|
||||
(void) protocol;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) {
|
||||
TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL);
|
||||
hidh_interface_t* p_hid = &_hidh_itf[idx];
|
||||
return (p_hid->daddr == daddr) ? p_hid : NULL;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline hidh_epbuf_t* get_hid_epbuf(uint8_t idx) {
|
||||
return &_hidh_epbuf[idx];
|
||||
}
|
||||
|
||||
// Get instance ID by endpoint address
|
||||
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr &&
|
||||
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
static hidh_interface_t* find_new_itf(void) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interface API
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == daddr) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_total_count(void) {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr != 0) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return p_hid->mounted;
|
||||
}
|
||||
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && info);
|
||||
|
||||
info->daddr = daddr;
|
||||
|
||||
// re-construct descriptor
|
||||
tusb_desc_interface_t* desc = &info->desc;
|
||||
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||
|
||||
desc->bInterfaceNumber = p_hid->itf_num;
|
||||
desc->bAlternateSetting = 0;
|
||||
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
|
||||
desc->bInterfaceClass = TUSB_CLASS_HID;
|
||||
desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE);
|
||||
desc->bInterfaceProtocol = p_hid->itf_protocol;
|
||||
desc->iInterface = 0; // not used yet
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||
}
|
||||
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->itf_protocol : 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->protocol_mode : 0;
|
||||
}
|
||||
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
if (XFER_RESULT_SUCCESS == xfer->result) {
|
||||
p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
}
|
||||
|
||||
tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
|
||||
}
|
||||
|
||||
void tuh_hid_set_default_protocol(uint8_t protocol) {
|
||||
_hidh_default_protocol = protocol;
|
||||
}
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_DRV("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 = itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
|
||||
|
||||
return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
|
||||
}
|
||||
|
||||
static void get_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Get Report complete\r\n");
|
||||
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
|
||||
bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_GET_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = get_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Set Report complete\r\n");
|
||||
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
|
||||
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
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_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = set_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
// SET IDLE request, device can stall if not support this request
|
||||
TU_LOG_DRV("HID Set Idle \r\n");
|
||||
|
||||
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_IDLE,
|
||||
.wValue = tu_htole16(idle_rate),
|
||||
.wIndex = tu_htole16((uint16_t) itf_num),
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if HID interface is ready to receive report
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in));
|
||||
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_in, epbuf->epin, p_hid->epin_size)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_in);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return tuh_edpt_abort_xfer(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_out);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) {
|
||||
TU_LOG_DRV("HID Send Report %d\r\n", report_id);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
|
||||
|
||||
if (p_hid->ep_out == 0) {
|
||||
// This HID does not have an out endpoint (other than control)
|
||||
return false;
|
||||
} else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) {
|
||||
// ep_out buffer is not large enough to hold contents
|
||||
return false;
|
||||
}
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_out));
|
||||
|
||||
if (report_id == 0) {
|
||||
// No report ID in transmission
|
||||
memcpy(&epbuf->epout[0], report, len);
|
||||
} else {
|
||||
epbuf->epout[0] = report_id;
|
||||
memcpy(&epbuf->epout[1], report, len);
|
||||
++len; // 1 more byte for report_id
|
||||
}
|
||||
|
||||
TU_LOG3_MEM(epbuf->epout, len, 2);
|
||||
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_out, epbuf->epout, len)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
bool hidh_init(void) {
|
||||
TU_LOG_DRV("sizeof(hidh_interface_t) = %u\r\n", sizeof(hidh_interface_t));
|
||||
tu_memclr(_hidh_itf, sizeof(_hidh_itf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
uint8_t const idx = get_idx_by_epaddr(daddr, ep_addr);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
|
||||
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
TU_LOG_DRV(" [idx=%u] Get Report callback\r\n", idx);
|
||||
TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2);
|
||||
tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
|
||||
} else {
|
||||
tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidh_close(uint8_t daddr) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
hidh_interface_t* p_hid = &_hidh_itf[i];
|
||||
if (p_hid->daddr == daddr) {
|
||||
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
|
||||
tuh_hid_umount_cb(daddr, i);
|
||||
tu_memclr(p_hid, sizeof(hidh_interface_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
(void) max_len;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
|
||||
TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(max_len >= drv_len);
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
|
||||
//------------- HID descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
|
||||
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
|
||||
|
||||
hidh_interface_t* p_hid = find_new_itf();
|
||||
TU_ASSERT(p_hid); // not enough interface, try to increase CFG_TUH_HID
|
||||
p_hid->daddr = daddr;
|
||||
|
||||
//------------- Endpoint Descriptors -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
|
||||
for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
|
||||
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
|
||||
|
||||
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
p_hid->ep_in = desc_ep->bEndpointAddress;
|
||||
p_hid->epin_size = tu_edpt_packet_size(desc_ep);
|
||||
} else {
|
||||
p_hid->ep_out = desc_ep->bEndpointAddress;
|
||||
p_hid->epout_size = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
}
|
||||
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
// Assume bNumDescriptors = 1
|
||||
p_hid->report_desc_type = desc_hid->bReportType;
|
||||
// Use offsetof to avoid pointer to the odd/misaligned address
|
||||
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*)desc_hid + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
|
||||
|
||||
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
|
||||
p_hid->protocol_mode = _hidh_default_protocol;
|
||||
if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) {
|
||||
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Set Configure
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
CONFG_SET_IDLE,
|
||||
CONFIG_SET_PROTOCOL,
|
||||
CONFIG_GET_REPORT_DESC,
|
||||
CONFIG_COMPLETE
|
||||
};
|
||||
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len);
|
||||
static void process_set_config(tuh_xfer_t* xfer);
|
||||
|
||||
bool hidh_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
|
||||
tuh_xfer_t xfer;
|
||||
xfer.daddr = daddr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.user_data = CONFG_SET_IDLE;
|
||||
|
||||
// fake request to kick-off the set config process
|
||||
process_set_config(&xfer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_set_config(tuh_xfer_t* xfer) {
|
||||
// Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well
|
||||
// therefore we could ignore its result
|
||||
if (!(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL)) {
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS,);
|
||||
}
|
||||
|
||||
uintptr_t const state = xfer->user_data;
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
switch (state) {
|
||||
case CONFG_SET_IDLE: {
|
||||
// Idle rate = 0 mean only report when there is changes
|
||||
const uint16_t idle_rate = 0;
|
||||
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE)
|
||||
? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||
_hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFIG_SET_PROTOCOL:
|
||||
_hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||
break;
|
||||
|
||||
case CONFIG_GET_REPORT_DESC:
|
||||
// Get Report Descriptor if possible
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) {
|
||||
TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(daddr, idx, NULL, 0);
|
||||
} else {
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0,
|
||||
usbh_get_enum_buf(), p_hid->report_desc_len,
|
||||
process_set_config, CONFIG_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_COMPLETE: {
|
||||
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||
|
||||
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid,);
|
||||
p_hid->mounted = true;
|
||||
|
||||
// enumeration is complete
|
||||
tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
|
||||
|
||||
// notify usbh that driver enumeration is complete
|
||||
usbh_driver_set_config_complete(daddr, p_hid->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 {
|
||||
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 size = header.size;
|
||||
if (size == 3) {
|
||||
size = 4; // HID 1.11 6.2.2.2 3 is 4 bytes
|
||||
}
|
||||
|
||||
uint8_t const data8 = (size > 0) ? desc_report[0] : 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;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < report_num; i++) {
|
||||
info = report_info_arr + i;
|
||||
TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||
}
|
||||
|
||||
return report_num;
|
||||
}
|
||||
|
||||
#endif
|
||||
185
managed_components/espressif__tinyusb/src/class/hid/hid_host.h
Normal file
185
managed_components/espressif__tinyusb/src/class/hid/hid_host.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_HID_HOST_H_
|
||||
#define _TUSB_HID_HOST_H_
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TODO Highspeed interrupt can be up to 512 bytes
|
||||
#ifndef CFG_TUH_HID_EPIN_BUFSIZE
|
||||
#define CFG_TUH_HID_EPIN_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_HID_EPOUT_BUFSIZE
|
||||
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint8_t usage;
|
||||
uint16_t usage_page;
|
||||
|
||||
// TODO still use the endpoint size for now
|
||||
// uint8_t in_len; // length of IN report
|
||||
// uint8_t out_len; // length of OUT report
|
||||
} tuh_hid_report_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interface API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the total number of mounted HID interfaces of a device
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
|
||||
|
||||
// Get all mounted interfaces across devices
|
||||
uint8_t tuh_hid_itf_get_total_count(void);
|
||||
|
||||
// backward compatible rename
|
||||
#define tuh_hid_instance_count tuh_hid_itf_get_count
|
||||
|
||||
// Get Interface information
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* itf_info);
|
||||
|
||||
// Get Interface index from device address + interface number
|
||||
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||
|
||||
// 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 idx);
|
||||
|
||||
// Check if HID interface is mounted
|
||||
bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Parse report descriptor into array of report_info struct and return number of reports.
|
||||
// For complicated report, application should write its own parser.
|
||||
TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
|
||||
uint8_t const* desc_report, uint16_t desc_len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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 idx);
|
||||
|
||||
// Device by default is enumerated in Boot protocol for simplicity. Application
|
||||
// can use this to modify the default protocol for next enumeration.
|
||||
void tuh_hid_set_default_protocol(uint8_t protocol);
|
||||
|
||||
// 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 idx, uint8_t protocol);
|
||||
|
||||
// Get Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||
|
||||
// Set Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
|
||||
void* report, uint16_t len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if HID interface is ready to receive report
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Try to receive next report on Interrupt Endpoint. Immediately return
|
||||
// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available
|
||||
// - false if failed to queue the transfer e.g endpoint is busy
|
||||
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Abort receiving report on Interrupt Endpoint
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Check if HID interface is ready to send report
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Send report using interrupt endpoint
|
||||
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
|
||||
bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Callbacks (Weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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 idx, uint8_t const* report_desc, uint16_t desc_len);
|
||||
|
||||
// Invoked when device with hid interface is un-mounted
|
||||
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Invoked when received report from device via interrupt endpoint
|
||||
// Note: if there is report ID (composite), it is 1st byte of report
|
||||
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when sent report to device successfully via interrupt endpoint
|
||||
void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when Get Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
|
||||
// Invoked when Sent Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
|
||||
// Invoked when Set Protocol request is complete
|
||||
void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
bool hidh_init(void);
|
||||
bool hidh_deinit(void);
|
||||
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
|
||||
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void hidh_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_HID_HOST_H_ */
|
||||
@@ -0,0 +1,111 @@
|
||||
# MIDI HOST DRIVER
|
||||
This README file contains the design notes and limitations of the
|
||||
MIDI host driver.
|
||||
|
||||
# MAXIMUM NUMBER OF MIDI DEVICES ATTACHED TO HOST
|
||||
In this version of the driver, only one MIDI device is supported. This
|
||||
constraint may change in the future.
|
||||
|
||||
# MAXIMUM NUMBER OF ENDPOINTS
|
||||
Although the USB MIDI 1.0 Class specification allows an arbitrary number
|
||||
of endpoints, this driver supports at most one USB BULK DATA IN endpoint
|
||||
and one USB BULK DATA OUT endpoint. Each endpoint can support up to 16
|
||||
virtual cables. If a device has multiple IN endpoints or multiple OUT
|
||||
endpoints, it will fail to enumerate.
|
||||
|
||||
Most USB MIDI devices contain both an IN endpoint and an OUT endpoint,
|
||||
but not all do. For example, some USB pedals only support an OUT endpoint.
|
||||
This driver allows that.
|
||||
|
||||
# PUBLIC API
|
||||
Applications interact with this driver via 8-bit buffers of MIDI messages
|
||||
formed using the rules for sending bytes on a 5-pin DIN cable per the
|
||||
original MIDI 1.0 specification.
|
||||
|
||||
To send a message to a device, the Host application composes a sequence
|
||||
of status and data bytes in a byte array and calls the API function.
|
||||
The arguments of the function are a pointer to the byte array, the number
|
||||
of bytes in the array, and the target virtual cable number 0-15.
|
||||
|
||||
When the host driver receives a message from the device, the host driver
|
||||
will call a callback function that the host application registers. This
|
||||
callback function contains a pointer to a message buffer, a message length,
|
||||
and the virtual cable number of the message buffer. One complete bulk IN
|
||||
endpoint transfer might contain multiple messages targeted to different
|
||||
virtual cables.
|
||||
|
||||
# SUBCLASS AUDIO CONTROL
|
||||
A MIDI device does not absolutely need to have an Audio Control Interface,
|
||||
unless it adheres to the USB Audio Class 2 spec, but many devices
|
||||
have them even if the devices do not have an audio streaming interface.
|
||||
Because this driver does not support audio streaming, the descriptor parser
|
||||
will skip past any audio control interface and audio streaming interface
|
||||
and open only the MIDI interface.
|
||||
|
||||
An audio streaming host driver can use this driver by passing a pointer
|
||||
to the MIDI interface descriptor that is found after the audio streaming
|
||||
interface to the midih_open() function. That is, an audio streaming host
|
||||
driver would parse the audio control interface descriptor and then the
|
||||
audio streaming interface and endpoint descriptors. When the next descriptor
|
||||
pointer points to a MIDI interface descriptor, call midih_open() with that
|
||||
descriptor pointer.
|
||||
|
||||
# CLASS SPECIFIC INTERFACE AND REQUESTS
|
||||
The host driver does not make use of the information in the class specific
|
||||
interface descriptors. In the future, a public API could be created to
|
||||
retrieve the string descriptors for the names of each ELEMENT,
|
||||
IN JACK and OUT JACK, and how the device describes the connections.
|
||||
|
||||
This driver also does not support class specific requests to control
|
||||
ELEMENT items, nor does it support non-MIDI Streaming bulk endpoints.
|
||||
|
||||
# MIDI CLASS SPECIFIC DESCRIPTOR TOTAL LENGTH FIELD IGNORED
|
||||
I have observed at least one keyboard by a leading manufacturer that
|
||||
sets the wTotalLength field of the Class-Specific MS Interface Header
|
||||
Descriptor to include the length of the MIDIStreaming Endpoint
|
||||
Descriptors. This is wrong per my reading of the specification.
|
||||
|
||||
# MESSAGE BUFFER DETAILS
|
||||
Messages buffers composed from USB data received on the IN endpoint will never contain
|
||||
running status because USB MIDI 1.0 class does not support that. Messages
|
||||
buffers to be sent to the device on the OUT endpoint may contain running status
|
||||
(the message might come from a UART data stream from a 5-pin DIN MIDI IN
|
||||
cable on the host, for example). The driver may in the future correctly compose
|
||||
4-byte USB MIDI Class packets using the running status if need be. However,
|
||||
it does not currently do that. Also, use of running status is not a good idea
|
||||
overall because a single byte error can really mess up the data stream with no
|
||||
way to recover until the next non-real time status byte is in the message buffer.
|
||||
|
||||
Message buffers to be sent to the device may contain Real time messages
|
||||
such as MIDI clock. Real time messages may be inserted in the message
|
||||
byte stream between status and data bytes of another message without disrupting
|
||||
the running status. However, because MIDI 1.0 class messages are sent
|
||||
as four byte packets, a real-time message so inserted will be re-ordered
|
||||
to be sent to the device in a new 4-byte packet immediately before the
|
||||
interrupted data stream.
|
||||
|
||||
Real time messages the device sends to the host can only appear between
|
||||
the status byte and data bytes of the message in System Exclusive messages
|
||||
that are longer than 3 bytes.
|
||||
|
||||
# POORLY FORMED USB MIDI DATA PACKETS FROM THE DEVICE
|
||||
Some devices do not properly encode the code index number (CIN) for the
|
||||
MIDI message status byte even though the 3-byte data payload correctly encodes
|
||||
the MIDI message. This driver looks to the byte after the CIN byte to decide
|
||||
how many bytes to place in the message buffer.
|
||||
|
||||
Some devices do not properly encode the virtual cable number. If the virtual
|
||||
cable number in the CIN data byte of the packet is not less than bNumEmbMIDIJack
|
||||
for that endpoint, then the host driver assumes virtual cable 0 and does not
|
||||
report an error.
|
||||
|
||||
Some MIDI devices will always send back exactly wMaxPacketSize bytes on
|
||||
every endpoint even if only one 4-byte packet is required (e.g., NOTE ON).
|
||||
These devices send packets with 4 packet bytes 0. This driver ignores all
|
||||
zero packets without reporting an error.
|
||||
|
||||
# ENUMERATION FAILURES
|
||||
The host may fail to enumerate a device if it has too many endpoints, if it has
|
||||
if it has a Standard MS Transfer Bulk Data Endpoint Descriptor (not supported),
|
||||
if it has a poorly formed descriptor, or if the descriptor is too long for
|
||||
the host to read the whole thing.
|
||||
201
managed_components/espressif__tinyusb/src/class/midi/midi.h
Normal file
201
managed_components/espressif__tinyusb/src/class/midi/midi.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MIDI_H_
|
||||
#define TUSB_MIDI_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Constants
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
MIDI_VERSION_1_0 = 0x0100,
|
||||
MIDI_VERSION_2_0 = 0x0200,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MIDI_CS_INTERFACE_HEADER = 0x01,
|
||||
MIDI_CS_INTERFACE_IN_JACK = 0x02,
|
||||
MIDI_CS_INTERFACE_OUT_JACK = 0x03,
|
||||
MIDI_CS_INTERFACE_ELEMENT = 0x04,
|
||||
} midi_cs_interface_subtype_t;
|
||||
|
||||
typedef enum {
|
||||
MIDI_CS_ENDPOINT_GENERAL = 0x01,
|
||||
MIDI_CS_ENDPOINT_GENERAL_2_0 = 0x02,
|
||||
} midi_cs_endpoint_subtype_t;
|
||||
|
||||
typedef enum {
|
||||
MIDI_JACK_EMBEDDED = 0x01,
|
||||
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_OFF = 8,
|
||||
MIDI_CIN_NOTE_ON = 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,
|
||||
};
|
||||
|
||||
enum {
|
||||
MIDI_MAX_DATA_VAL = 0x7F,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Specific Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// MIDI Interface Header Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType; ///< must be TUSB_DESC_CS_INTERFACE
|
||||
uint8_t bDescriptorSubType;///< Descriptor SubType
|
||||
uint16_t bcdMSC; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
|
||||
uint16_t wTotalLength;
|
||||
} midi_desc_header_t;
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_header_t) == 7, "size is not correct");
|
||||
|
||||
/// MIDI In Jack Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType; ///< Descriptor Type, must be Class-Specific
|
||||
uint8_t bDescriptorSubType;///< Descriptor SubType
|
||||
uint8_t bJackType; ///< Embedded or External
|
||||
uint8_t bJackID; ///< Unique ID for MIDI IN Jack
|
||||
uint8_t iJack; ///< string descriptor
|
||||
} midi_desc_in_jack_t;
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_in_jack_t) == 6, "size is not correct");
|
||||
|
||||
/// MIDI Out Jack Descriptor with multiple input pins
|
||||
#define midi_desc_out_jack_n_t(input_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bJackType; \
|
||||
uint8_t bJackID; \
|
||||
uint8_t bNrInputPins; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} input[input_num]; \
|
||||
uint8_t iJack; \
|
||||
}
|
||||
|
||||
typedef midi_desc_out_jack_n_t(1) midi_desc_out_jack_1in_t; // 1 input
|
||||
typedef midi_desc_out_jack_1in_t midi_desc_out_jack_t; // backward compatible
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_out_jack_1in_t) == 7 + 2 * 1, "size is not correct");
|
||||
|
||||
/// MIDI Element Descriptor with multiple pins
|
||||
#define midi_desc_element_n_t(input_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bElementID; \
|
||||
uint8_t bNrInputPins; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} pins[input_num]; \
|
||||
uint8_t bNrOutputPins; \
|
||||
uint8_t bInTerminalLink; \
|
||||
uint8_t bOutTerminalLink; \
|
||||
uint8_t bElCapsSize; \
|
||||
uint16_t bmElementCaps; \
|
||||
uint8_t iElement; \
|
||||
}
|
||||
|
||||
// This descriptor follows the standard bulk data endpoint descriptor
|
||||
#define midi_desc_cs_endpoint_n_t(jack_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bNumEmbMIDIJack; \
|
||||
uint8_t baAssocJackID[jack_num]; \
|
||||
}
|
||||
|
||||
typedef midi_desc_cs_endpoint_n_t() midi_desc_cs_endpoint_t; // empty/flexible jack list
|
||||
typedef midi_desc_cs_endpoint_n_t(1) midi_desc_cs_endpoint_1jack_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// For Internal Driver Use
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t buffer[4];
|
||||
uint8_t index;
|
||||
uint8_t total;
|
||||
} midi_driver_stream_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_MIDI)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "midi_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
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.
|
||||
midi_driver_stream_t stream_write;
|
||||
midi_driver_stream_t stream_read;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
// FIFO
|
||||
tu_fifo_t rx_ff;
|
||||
tu_fifo_t tx_ff;
|
||||
uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
osal_mutex_def_t rx_ff_mutex;
|
||||
osal_mutex_def_t tx_ff_mutex;
|
||||
#endif
|
||||
} midid_interface_t;
|
||||
|
||||
#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
|
||||
|
||||
// Endpoint Transfer buffer
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||
TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||
} _midid_epbuf[CFG_TUD_MIDI];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static midid_interface_t _midid_itf[CFG_TUD_MIDI];
|
||||
|
||||
bool tud_midi_n_mounted (uint8_t itf) {
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
return midi->ep_in && midi->ep_out;
|
||||
}
|
||||
|
||||
static void _prep_out_transaction(uint8_t idx) {
|
||||
const uint8_t rhport = 0;
|
||||
midid_interface_t* p_midi = &_midid_itf[idx];
|
||||
uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
||||
// and slowly move it to the FIFO when read().
|
||||
// This pre-check reduces endpoint claiming
|
||||
TU_VERIFY(available >= CFG_TUD_MIDI_EP_BUFSIZE, );
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
|
||||
|
||||
// fifo can be changed before endpoint is claimed
|
||||
available = tu_fifo_remaining(&p_midi->rx_ff);
|
||||
|
||||
if ( available >= CFG_TUD_MIDI_EP_BUFSIZE ) {
|
||||
usbd_edpt_xfer(rhport, p_midi->ep_out, _midid_epbuf[idx].epout, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||
}else
|
||||
{
|
||||
// Release endpoint since we don't make any transfer
|
||||
usbd_edpt_release(rhport, p_midi->ep_out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf) {
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// READ API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
|
||||
{
|
||||
(void) cable_num;
|
||||
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
const midi_driver_stream_t* stream = &midi->stream_read;
|
||||
|
||||
// when using with packet API stream total & index are both zero
|
||||
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
|
||||
}
|
||||
|
||||
uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
(void) cable_num;
|
||||
TU_VERIFY(bufsize, 0);
|
||||
|
||||
uint8_t* buf8 = (uint8_t*) buffer;
|
||||
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
midi_driver_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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy data up to bufsize
|
||||
uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize);
|
||||
|
||||
// Skip the header (1st byte) in the buffer
|
||||
TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
|
||||
|
||||
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);
|
||||
|
||||
const uint32_t num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
|
||||
_prep_out_transaction(itf);
|
||||
return (num_read == 4);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// WRITE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static uint32_t write_flush(uint8_t idx) {
|
||||
midid_interface_t* midi = &_midid_itf[idx];
|
||||
|
||||
if (!tu_fifo_count(&midi->tx_ff)) {
|
||||
return 0; // No data to send
|
||||
}
|
||||
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
// skip if previous transfer not complete
|
||||
TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
|
||||
|
||||
uint16_t count = tu_fifo_read_n(&midi->tx_ff, _midid_epbuf[idx].epin, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||
|
||||
if (count) {
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, _midid_epbuf[idx].epin, count), 0 );
|
||||
return count;
|
||||
}else {
|
||||
// Release endpoint since we don't make any transfer
|
||||
usbd_edpt_release(rhport, midi->ep_in);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t* buffer, uint32_t bufsize)
|
||||
{
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
TU_VERIFY(midi->ep_in, 0);
|
||||
|
||||
midi_driver_stream_t* stream = &midi->stream_write;
|
||||
|
||||
uint32_t i = 0;
|
||||
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
|
||||
{
|
||||
const uint8_t data = buffer[i];
|
||||
i++;
|
||||
|
||||
if ( stream->index == 0 )
|
||||
{
|
||||
//------------- New event packet -------------//
|
||||
const uint8_t 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]) & 0xF) == MIDI_CIN_SYSEX_START )
|
||||
{
|
||||
if ( data == MIDI_STATUS_SYSEX_END )
|
||||
{
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
|
||||
stream->total = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->total = 4;
|
||||
}
|
||||
}
|
||||
else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
|
||||
{
|
||||
// Channel Voice Messages
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 4;
|
||||
}
|
||||
else if ( msg == 0xC || msg == 0xD)
|
||||
{
|
||||
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 3;
|
||||
}
|
||||
else if ( msg == 0xf )
|
||||
{
|
||||
// System message
|
||||
if ( data == MIDI_STATUS_SYSEX_START )
|
||||
{
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_START;
|
||||
stream->total = 4;
|
||||
}
|
||||
else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT )
|
||||
{
|
||||
stream->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;
|
||||
}
|
||||
stream->buffer[0] |= (uint8_t)(cable_num << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pack individual bytes if we don't support packing them into words.
|
||||
stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
|
||||
stream->buffer[2] = 0;
|
||||
stream->buffer[3] = 0;
|
||||
stream->index = 2;
|
||||
stream->total = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//------------- On-going (buffering) packet -------------//
|
||||
|
||||
TU_ASSERT(stream->index < 4, i);
|
||||
stream->buffer[stream->index] = data;
|
||||
stream->index++;
|
||||
|
||||
// See if this byte ends a SysEx.
|
||||
if ( (stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
|
||||
{
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
|
||||
stream->total = stream->index;
|
||||
}
|
||||
}
|
||||
|
||||
// Send out packet
|
||||
if ( stream->index == stream->total )
|
||||
{
|
||||
// zeroes unused bytes
|
||||
for (uint8_t idx = stream->total; idx < 4; idx++) {
|
||||
stream->buffer[idx] = 0;
|
||||
}
|
||||
|
||||
const uint16_t count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
write_flush(itf);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
|
||||
midid_interface_t* midi = &_midid_itf[itf];
|
||||
TU_VERIFY(midi->ep_in);
|
||||
|
||||
if (tu_fifo_remaining(&midi->tx_ff) < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tu_fifo_write_n(&midi->tx_ff, packet, 4);
|
||||
write_flush(itf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void midid_init(void) {
|
||||
tu_memclr(_midid_itf, sizeof(_midid_itf));
|
||||
|
||||
for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
|
||||
midid_interface_t* midi = &_midid_itf[i];
|
||||
|
||||
// config fifo
|
||||
tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, false); // true, true
|
||||
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&midi->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&midi->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_wr != NULL && mutex_wr != NULL, );
|
||||
|
||||
tu_fifo_config_mutex(&midi->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&midi->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool midid_deinit(void) {
|
||||
#if CFG_FIFO_MUTEX
|
||||
for(uint8_t i=0; i<CFG_TUD_MIDI; i++) {
|
||||
midid_interface_t* midi = &_midid_itf[i];
|
||||
osal_mutex_t mutex_rd = midi->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = midi->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&midi->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&midi->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void midid_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
|
||||
{
|
||||
midid_interface_t* midi = &_midid_itf[i];
|
||||
tu_memclr(midi, ITF_MEM_RESET_SIZE);
|
||||
tu_fifo_clear(&midi->rx_ff);
|
||||
tu_fifo_clear(&midi->tx_ff);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
|
||||
uint16_t drv_len = 0;
|
||||
uint8_t const * p_desc = (uint8_t const *)desc_itf;
|
||||
|
||||
// 1st Interface is Audio Control v1 (optional)
|
||||
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
|
||||
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
|
||||
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
|
||||
drv_len = tu_desc_len(desc_itf);
|
||||
p_desc = tu_desc_next(desc_itf);
|
||||
// Skip Class Specific descriptors
|
||||
while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd Interface is MIDI Streaming
|
||||
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
|
||||
const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
|
||||
AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
|
||||
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
|
||||
|
||||
// Find available interface
|
||||
midid_interface_t * p_midi = NULL;
|
||||
uint8_t idx;
|
||||
for(idx=0; idx<CFG_TUD_MIDI; idx++) {
|
||||
if ( _midid_itf[idx].ep_in == 0 && _midid_itf[idx].ep_out == 0 ) {
|
||||
p_midi = &_midid_itf[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(p_midi);
|
||||
|
||||
p_midi->itf_num = desc_midi->bInterfaceNumber;
|
||||
(void) p_midi->itf_num;
|
||||
|
||||
// next descriptor
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
// Find and open endpoint descriptors
|
||||
uint8_t found_endpoints = 0;
|
||||
while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
|
||||
{
|
||||
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
||||
{
|
||||
TU_ASSERT(usbd_edpt_open(rhport, (const tusb_desc_endpoint_t*) p_desc), 0);
|
||||
uint8_t ep_addr = ((const tusb_desc_endpoint_t*) p_desc)->bEndpointAddress;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// Prepare for incoming data
|
||||
_prep_out_transaction(idx);
|
||||
|
||||
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 midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
|
||||
(void) rhport; (void) stage; (void) request;
|
||||
return false; // driver doesn't support any request yet
|
||||
}
|
||||
|
||||
bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) result;
|
||||
(void) rhport;
|
||||
|
||||
uint8_t idx;
|
||||
midid_interface_t* p_midi;
|
||||
|
||||
// Identify which interface to use
|
||||
for (idx = 0; idx < CFG_TUD_MIDI; idx++) {
|
||||
p_midi = &_midid_itf[idx];
|
||||
if ((ep_addr == p_midi->ep_out) || (ep_addr == p_midi->ep_in)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(idx < CFG_TUD_MIDI);
|
||||
|
||||
// receive new data
|
||||
if (ep_addr == p_midi->ep_out) {
|
||||
tu_fifo_write_n(&p_midi->rx_ff, _midid_epbuf[idx].epout, (uint16_t)xferred_bytes);
|
||||
|
||||
// invoke receive callback if available
|
||||
tud_midi_rx_cb(idx);
|
||||
|
||||
// prepare for next
|
||||
// TODO for now ep_out is not used by public API therefore there is no race condition,
|
||||
// and does not need to claim like ep_in
|
||||
_prep_out_transaction(idx);
|
||||
} else if (ep_addr == p_midi->ep_in) {
|
||||
if (0 == write_flush(idx)) {
|
||||
// If there is no data left, a ZLP should be sent if
|
||||
// xferred_bytes is multiple of EP size and not zero
|
||||
if (!tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE))) {
|
||||
if (usbd_edpt_claim(rhport, p_midi->ep_in)) {
|
||||
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_MIDI_DEVICE_H_
|
||||
#define _TUSB_MIDI_DEVICE_H_
|
||||
|
||||
#include "class/audio/audio.h"
|
||||
#include "midi.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
|
||||
#warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
|
||||
#define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MIDI_EP_BUFSIZE
|
||||
#define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \addtogroup MIDI_Serial Serial
|
||||
* @{
|
||||
* \defgroup MIDI_Serial_Device Device
|
||||
* @{ */
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Interfaces)
|
||||
// CFG_TUD_MIDI > 1
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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 (Single Interface)
|
||||
//--------------------------------------------------------------------+
|
||||
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 (optional)
|
||||
//--------------------------------------------------------------------+
|
||||
void tud_midi_rx_cb(uint8_t itf);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static inline bool tud_midi_mounted (void)
|
||||
{
|
||||
return tud_midi_n_mounted(0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_midi_available (void)
|
||||
{
|
||||
return tud_midi_n_available(0, 0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
|
||||
{
|
||||
return tud_midi_n_stream_read(0, 0, buffer, bufsize);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
|
||||
{
|
||||
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
|
||||
}
|
||||
|
||||
static inline bool tud_midi_packet_read (uint8_t packet[4])
|
||||
{
|
||||
return tud_midi_n_packet_read(0, packet);
|
||||
}
|
||||
|
||||
static inline bool tud_midi_packet_write (uint8_t const packet[4])
|
||||
{
|
||||
return tud_midi_n_packet_write(0, packet);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void midid_init (void);
|
||||
bool midid_deinit (void);
|
||||
void midid_reset (uint8_t rhport);
|
||||
uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_MIDI_DEVICE_H_ */
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
649
managed_components/espressif__tinyusb/src/class/midi/midi_host.c
Normal file
649
managed_components/espressif__tinyusb/src/class/midi/midi_host.c
Normal file
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_MIDI)
|
||||
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
|
||||
#include "midi_host.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUH_MIDI_LOG_LEVEL
|
||||
#define CFG_TUH_MIDI_LOG_LEVEL CFG_TUH_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MIDI_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data) { (void) idx; (void) desc_cb_data; }
|
||||
TU_ATTR_WEAK void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data) { (void) idx; (void) mount_cb_data; }
|
||||
TU_ATTR_WEAK void tuh_midi_umount_cb(uint8_t idx) { (void) idx; }
|
||||
TU_ATTR_WEAK void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
|
||||
TU_ATTR_WEAK void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t bInterfaceNumber; // interface number of MIDI streaming
|
||||
uint8_t iInterface;
|
||||
uint8_t itf_count; // number of interface including Audio Control + MIDI streaming
|
||||
|
||||
uint8_t ep_in; // IN endpoint address
|
||||
uint8_t ep_out; // OUT endpoint address
|
||||
|
||||
uint8_t rx_cable_count; // IN endpoint CS descriptor bNumEmbMIDIJack value
|
||||
uint8_t tx_cable_count; // OUT endpoint CS descriptor bNumEmbMIDIJack value
|
||||
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
// 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.
|
||||
midi_driver_stream_t stream_write;
|
||||
midi_driver_stream_t stream_read;
|
||||
#endif
|
||||
|
||||
// Endpoint stream
|
||||
struct {
|
||||
tu_edpt_stream_t tx;
|
||||
tu_edpt_stream_t rx;
|
||||
|
||||
uint8_t rx_ff_buf[CFG_TUH_MIDI_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUH_MIDI_TX_BUFSIZE];
|
||||
} ep_stream;
|
||||
|
||||
bool mounted;
|
||||
}midih_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS);
|
||||
TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS);
|
||||
} midih_epbuf_t;
|
||||
|
||||
static midih_interface_t _midi_host[CFG_TUH_MIDI];
|
||||
CFG_TUH_MEM_SECTION static midih_epbuf_t _midi_epbuf[CFG_TUH_MIDI];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t find_new_midi_index(void) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
if (_midi_host[idx].daddr == 0) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
const midih_interface_t *p_midi = &_midi_host[idx];
|
||||
if ((p_midi->daddr == daddr) &&
|
||||
(ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_init(void) {
|
||||
tu_memclr(&_midi_host, sizeof(_midi_host));
|
||||
for (int inst = 0; inst < CFG_TUH_MIDI; inst++) {
|
||||
midih_interface_t *p_midi_host = &_midi_host[inst];
|
||||
tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false,
|
||||
p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS);
|
||||
tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false,
|
||||
p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool midih_deinit(void) {
|
||||
for (size_t i = 0; i < CFG_TUH_MIDI; i++) {
|
||||
midih_interface_t* p_midi = &_midi_host[i];
|
||||
tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
|
||||
tu_edpt_stream_deinit(&p_midi->ep_stream.tx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void midih_close(uint8_t daddr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
midih_interface_t* p_midi = &_midi_host[idx];
|
||||
if (p_midi->daddr == daddr) {
|
||||
TU_LOG_DRV(" MIDI close addr = %u index = %u\r\n", daddr, idx);
|
||||
tuh_midi_umount_cb(idx);
|
||||
|
||||
p_midi->ep_in = 0;
|
||||
p_midi->ep_out = 0;
|
||||
p_midi->bInterfaceNumber = 0;
|
||||
p_midi->rx_cable_count = 0;
|
||||
p_midi->tx_cable_count = 0;
|
||||
p_midi->daddr = 0;
|
||||
p_midi->mounted = false;
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
tu_memclr(&p_midi->stream_read, sizeof(p_midi->stream_read));
|
||||
tu_memclr(&p_midi->stream_write, sizeof(p_midi->stream_write));
|
||||
#endif
|
||||
tu_edpt_stream_close(&p_midi->ep_stream.rx);
|
||||
tu_edpt_stream_close(&p_midi->ep_stream.tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
|
||||
// receive new data, put it into FIFO and invoke callback if available
|
||||
// Note: some devices send back all zero packets even if there is no data ready
|
||||
if (xferred_bytes && !tu_mem_is_zero(p_midi->ep_stream.rx.ep_buf, xferred_bytes)) {
|
||||
tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
|
||||
tuh_midi_rx_cb(idx, xferred_bytes);
|
||||
}
|
||||
|
||||
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for next transfer
|
||||
} else if (ep_addr == p_midi->ep_stream.tx.ep_addr) {
|
||||
tuh_midi_tx_cb(idx, xferred_bytes);
|
||||
|
||||
if (0 == tu_edpt_stream_write_xfer(dev_addr, &p_midi->ep_stream.tx)) {
|
||||
// If there is no data left, a ZLP should be sent if
|
||||
// xferred_bytes is multiple of EP size and not zero
|
||||
tu_edpt_stream_write_zlp_if_needed(dev_addr, &p_midi->ep_stream.tx, xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
|
||||
const uint8_t *p_end = ((const uint8_t *) desc_itf) + max_len;
|
||||
const uint8_t *p_desc = (const uint8_t *) desc_itf;
|
||||
|
||||
const uint8_t idx = find_new_midi_index();
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
p_midi->itf_count = 0;
|
||||
|
||||
tuh_midi_descriptor_cb_t desc_cb = { 0 };
|
||||
desc_cb.jack_num = 0;
|
||||
|
||||
// There can be just a MIDI or an Audio + MIDI interface
|
||||
// - If there is Audio Control Interface + Audio Header descriptor, then skip it.
|
||||
// - If there is an Audio Control Interface + Audio Streaming Interface, then ignore the Audio Streaming Interface.
|
||||
// Future:
|
||||
// Note that if this driver is used with an USB Audio Streaming host driver,
|
||||
// then call that driver first. If the MIDI interface comes before the
|
||||
// audio streaming interface, then the audio driver will have to call this
|
||||
// driver after parsing the audio control interface and then resume parsing
|
||||
// the streaming audio interface.
|
||||
if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
|
||||
TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(audio_desc_cs_ac_interface_t));
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
|
||||
tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_HEADER);
|
||||
desc_cb.desc_audio_control = desc_itf;
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_itf = (const tusb_desc_interface_t *)p_desc;
|
||||
p_midi->itf_count = 1;
|
||||
// skip non-interface and non-midi streaming descriptors
|
||||
while (tu_desc_in_bounds(p_desc, p_end) &&
|
||||
(desc_itf->bDescriptorType != TUSB_DESC_INTERFACE || (desc_itf->bInterfaceClass == TUSB_CLASS_AUDIO && desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING))) {
|
||||
if (desc_itf->bDescriptorType == TUSB_DESC_INTERFACE && desc_itf->bAlternateSetting == 0) {
|
||||
p_midi->itf_count++;
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_itf = (tusb_desc_interface_t const *)p_desc;
|
||||
}
|
||||
TU_VERIFY(p_desc < p_end); // TODO: If MIDI interface comes after Audio Streaming, then max_len did not include the MIDI interface descriptor
|
||||
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
|
||||
}
|
||||
TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass);
|
||||
|
||||
TU_LOG_DRV("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
|
||||
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
|
||||
p_midi->iInterface = desc_itf->iInterface;
|
||||
p_midi->itf_count++;
|
||||
desc_cb.desc_midi = desc_itf;
|
||||
|
||||
p_desc = tu_desc_next(p_desc); // next to CS Header
|
||||
|
||||
bool found_new_interface = false;
|
||||
while (tu_desc_in_bounds(p_desc, p_end) && !found_new_interface) {
|
||||
switch (tu_desc_type(p_desc)) {
|
||||
case TUSB_DESC_INTERFACE:
|
||||
found_new_interface = true;
|
||||
break;
|
||||
|
||||
case TUSB_DESC_CS_INTERFACE:
|
||||
switch (tu_desc_subtype(p_desc)) {
|
||||
case MIDI_CS_INTERFACE_HEADER:
|
||||
TU_LOG_DRV(" Interface Header descriptor\r\n");
|
||||
desc_cb.desc_header = p_desc;
|
||||
break;
|
||||
|
||||
case MIDI_CS_INTERFACE_IN_JACK:
|
||||
case MIDI_CS_INTERFACE_OUT_JACK: {
|
||||
TU_LOG_DRV(" Jack %s %s descriptor \r\n",
|
||||
tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_IN_JACK ? "IN" : "OUT",
|
||||
p_desc[3] == MIDI_JACK_EXTERNAL ? "External" : "Embedded");
|
||||
if (desc_cb.jack_num < TU_ARRAY_SIZE(desc_cb.desc_jack)) {
|
||||
desc_cb.desc_jack[desc_cb.jack_num++] = p_desc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MIDI_CS_INTERFACE_ELEMENT:
|
||||
TU_LOG_DRV(" Element descriptor\r\n");
|
||||
desc_cb.desc_element = p_desc;
|
||||
break;
|
||||
|
||||
default:
|
||||
TU_LOG_DRV(" Unknown CS Interface sub-type %u\r\n", tu_desc_subtype(p_desc));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_DESC_ENDPOINT: {
|
||||
const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
|
||||
p_desc = tu_desc_next(p_desc); // next to CS endpoint
|
||||
TU_VERIFY(p_desc < p_end && tu_desc_next(p_desc) <= p_end);
|
||||
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
|
||||
|
||||
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
|
||||
if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
|
||||
p_midi->ep_out = p_ep->bEndpointAddress;
|
||||
p_midi->tx_cable_count = p_csep->bNumEmbMIDIJack;
|
||||
desc_cb.desc_epout = p_ep;
|
||||
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
|
||||
tu_edpt_stream_open(&p_midi->ep_stream.tx, p_ep);
|
||||
} else {
|
||||
p_midi->ep_in = p_ep->bEndpointAddress;
|
||||
p_midi->rx_cable_count = p_csep->bNumEmbMIDIJack;
|
||||
desc_cb.desc_epin = p_ep;
|
||||
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
|
||||
tu_edpt_stream_open(&p_midi->ep_stream.rx, p_ep);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break; // skip unknown descriptor
|
||||
}
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
desc_cb.desc_midi_total_len = (uint16_t) ((uintptr_t)p_desc - (uintptr_t) desc_itf);
|
||||
|
||||
p_midi->daddr = dev_addr;
|
||||
tuh_midi_descriptor_cb(idx, &desc_cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
||||
uint8_t idx = tuh_midi_itf_get_index(dev_addr, itf_num);
|
||||
TU_ASSERT(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
p_midi->mounted = true;
|
||||
|
||||
const tuh_midi_mount_cb_t mount_cb_data = {
|
||||
.daddr = dev_addr,
|
||||
.bInterfaceNumber = itf_num,
|
||||
.rx_cable_count = p_midi->rx_cable_count,
|
||||
.tx_cable_count = p_midi->tx_cable_count,
|
||||
};
|
||||
tuh_midi_mount_cb(idx, &mount_cb_data);
|
||||
|
||||
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for incoming data
|
||||
|
||||
// No special config things to do for MIDI
|
||||
usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tuh_midi_mounted(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return p_midi->mounted;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
|
||||
const midih_interface_t *p_midi = &_midi_host[idx];
|
||||
if (p_midi->daddr == daddr &&
|
||||
(p_midi->bInterfaceNumber == itf_num ||
|
||||
p_midi->bInterfaceNumber == (uint8_t) (itf_num + p_midi->itf_count - 1))) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
|
||||
midih_interface_t* p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi && info);
|
||||
|
||||
info->daddr = p_midi->daddr;
|
||||
|
||||
// re-construct descriptor
|
||||
tusb_desc_interface_t* desc = &info->desc;
|
||||
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||
|
||||
desc->bInterfaceNumber = p_midi->bInterfaceNumber;
|
||||
desc->bAlternateSetting = 0;
|
||||
desc->bNumEndpoints = (uint8_t)((p_midi->ep_in != 0 ? 1:0) + (p_midi->ep_out != 0 ? 1:0));
|
||||
desc->bInterfaceClass = TUSB_CLASS_AUDIO;
|
||||
desc->bInterfaceSubClass = AUDIO_SUBCLASS_MIDI_STREAMING;
|
||||
desc->bInterfaceProtocol = 0;
|
||||
desc->iInterface = p_midi->iInterface;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_get_tx_cable_count (uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi->ep_stream.tx.ep_addr != 0, 0);
|
||||
return p_midi->tx_cable_count;
|
||||
}
|
||||
|
||||
uint8_t tuh_midi_get_rx_cable_count (uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(p_midi->ep_stream.rx.ep_addr != 0, 0);
|
||||
return p_midi->rx_cable_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_read_available(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return tu_edpt_stream_read_available(&p_midi->ep_stream.rx);
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_write_flush(uint8_t idx) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
return tu_edpt_stream_write_xfer(p_midi->daddr, &p_midi->ep_stream.tx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Packet API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
uint32_t count4 = tu_min32(bufsize, tu_edpt_stream_read_available(&p_midi->ep_stream.rx));
|
||||
count4 = tu_align4(count4); // round down to multiple of 4
|
||||
TU_VERIFY(count4 > 0, 0);
|
||||
return tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, buffer, count4);
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
|
||||
const uint32_t bufsize4 = tu_align4(bufsize);
|
||||
TU_VERIFY(bufsize4 > 0, 0);
|
||||
return tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, buffer, bufsize4);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
TU_VERIFY(cable_num < p_midi->tx_cable_count);
|
||||
midi_driver_stream_t *stream = &p_midi->stream_write;
|
||||
|
||||
uint32_t byte_count = 0;
|
||||
while ((byte_count < bufsize) && (tu_edpt_stream_write_available(p_midi->daddr, &p_midi->ep_stream.tx) >= 4)) {
|
||||
uint8_t const data = buffer[byte_count];
|
||||
byte_count++;
|
||||
if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
|
||||
// real-time messages need to be sent right away
|
||||
midi_driver_stream_t streamrt;
|
||||
streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
|
||||
streamrt.buffer[1] = data;
|
||||
streamrt.index = 2;
|
||||
streamrt.total = 2;
|
||||
uint32_t const count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, streamrt.buffer, 4);
|
||||
TU_ASSERT(count == 4, byte_count); // Check FIFO overflown, since we already check fifo remaining. It is probably race condition
|
||||
} else if (stream->index == 0) {
|
||||
//------------- New event packet -------------//
|
||||
|
||||
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] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 4;
|
||||
} else if (msg == 0xC || msg == 0xD) {
|
||||
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
|
||||
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||
stream->total = 3;
|
||||
} else if (msg == 0xf) {
|
||||
// System message
|
||||
if (data == MIDI_STATUS_SYSEX_START) {
|
||||
stream->buffer[0] = MIDI_CIN_SYSEX_START;
|
||||
stream->total = 4;
|
||||
} else if (data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT) {
|
||||
stream->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] = (uint8_t) (cable_num << 4 | 0xf);
|
||||
stream->buffer[2] = 0;
|
||||
stream->buffer[3] = 0;
|
||||
stream->index = 2;
|
||||
stream->total = 2;
|
||||
}
|
||||
} else {
|
||||
//------------- On-going (buffering) packet -------------//
|
||||
TU_ASSERT(stream->index < 4, byte_count);
|
||||
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 >= 2 && stream->index == stream->total) {
|
||||
// zeroes unused bytes
|
||||
for (uint8_t i = stream->total; i < 4; i++) {
|
||||
stream->buffer[i] = 0;
|
||||
}
|
||||
TU_LOG3_MEM(stream->buffer, 4, 2);
|
||||
|
||||
const uint32_t count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, stream->buffer, 4);
|
||||
|
||||
// complete current event packet, reset stream
|
||||
stream->index = 0;
|
||||
stream->total = 0;
|
||||
|
||||
// FIFO overflown, since we already check fifo remaining. It is probably race condition
|
||||
TU_ASSERT(count == 4, byte_count);
|
||||
}
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize) {
|
||||
TU_VERIFY(idx < CFG_TUH_MIDI && p_cable_num && p_buffer && bufsize > 0);
|
||||
midih_interface_t *p_midi = &_midi_host[idx];
|
||||
uint32_t bytes_buffered = 0;
|
||||
uint8_t one_byte;
|
||||
if (!tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
|
||||
return 0;
|
||||
}
|
||||
*p_cable_num = (one_byte >> 4) & 0xf;
|
||||
uint32_t nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
static uint16_t cable_sysex_in_progress;// bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END
|
||||
while (nread == 4 && bytes_buffered < bufsize) {
|
||||
*p_cable_num = (p_midi->stream_read.buffer[0] >> 4) & 0x0f;
|
||||
uint8_t bytes_to_add_to_stream = 0;
|
||||
if (*p_cable_num < p_midi->rx_cable_count) {
|
||||
// ignore the CIN field; too many devices out there encode this wrong
|
||||
uint8_t status = p_midi->stream_read.buffer[1];
|
||||
uint16_t cable_mask = (uint16_t) (1 << *p_cable_num);
|
||||
if (status <= MIDI_MAX_DATA_VAL || status == MIDI_STATUS_SYSEX_START) {
|
||||
if (status == MIDI_STATUS_SYSEX_START) {
|
||||
cable_sysex_in_progress |= cable_mask;
|
||||
}
|
||||
// only add the packet if a sysex message is in progress
|
||||
if (cable_sysex_in_progress & cable_mask) {
|
||||
++bytes_to_add_to_stream;
|
||||
for (uint8_t i = 2; i < 4; i++) {
|
||||
if (p_midi->stream_read.buffer[i] <= MIDI_MAX_DATA_VAL) {
|
||||
++bytes_to_add_to_stream;
|
||||
} else if (p_midi->stream_read.buffer[i] == MIDI_STATUS_SYSEX_END) {
|
||||
++bytes_to_add_to_stream;
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
i = 4;// force the loop to exit; I hate break statements in loops
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// bad packet discard
|
||||
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
continue;
|
||||
}
|
||||
} else if (status < MIDI_STATUS_SYSEX_START) {
|
||||
// then it is a channel message either three bytes or two
|
||||
uint8_t fake_cin = (status & 0xf0) >> 4;
|
||||
switch (fake_cin) {
|
||||
case MIDI_CIN_NOTE_OFF:
|
||||
case MIDI_CIN_NOTE_ON:
|
||||
case MIDI_CIN_POLY_KEYPRESS:
|
||||
case MIDI_CIN_CONTROL_CHANGE:
|
||||
case MIDI_CIN_PITCH_BEND_CHANGE:
|
||||
bytes_to_add_to_stream = 3;
|
||||
break;
|
||||
case MIDI_CIN_PROGRAM_CHANGE:
|
||||
case MIDI_CIN_CHANNEL_PRESSURE:
|
||||
bytes_to_add_to_stream = 2;
|
||||
break;
|
||||
default:
|
||||
break;// Should not get this
|
||||
}
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
} else if (status < MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
|
||||
switch (status) {
|
||||
case MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME:
|
||||
case MIDI_STATUS_SYSCOM_SONG_SELECT:
|
||||
bytes_to_add_to_stream = 2;
|
||||
break;
|
||||
case MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER:
|
||||
bytes_to_add_to_stream = 3;
|
||||
break;
|
||||
case MIDI_STATUS_SYSCOM_TUNE_REQUEST:
|
||||
case MIDI_STATUS_SYSEX_END:
|
||||
bytes_to_add_to_stream = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
|
||||
} else {
|
||||
// Real-time message: can be inserted into a sysex message,
|
||||
// so do don't clear cable_sysex_in_progress bit
|
||||
bytes_to_add_to_stream = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// bad packet discard
|
||||
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint8_t i = 1; i <= bytes_to_add_to_stream; i++) {
|
||||
*p_buffer++ = p_midi->stream_read.buffer[i];
|
||||
}
|
||||
bytes_buffered += bytes_to_add_to_stream;
|
||||
nread = 0;
|
||||
if (tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
|
||||
uint8_t new_cable = (one_byte >> 4) & 0xf;
|
||||
if (new_cable == *p_cable_num) {
|
||||
// still on the same cable. Continue reading the stream
|
||||
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_buffered;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
193
managed_components/espressif__tinyusb/src/class/midi/midi_host.h
Normal file
193
managed_components/espressif__tinyusb/src/class/midi/midi_host.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MIDI_HOST_H_
|
||||
#define TUSB_MIDI_HOST_H_
|
||||
|
||||
#include "class/audio/audio.h"
|
||||
#include "midi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
|
||||
#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
|
||||
#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
|
||||
#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
|
||||
// Disable this can save driver footprint.
|
||||
#ifndef CFG_TUH_MIDI_STREAM_API
|
||||
#define CFG_TUH_MIDI_STREAM_API 1
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Types
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
const tusb_desc_interface_t* desc_audio_control;
|
||||
const tusb_desc_interface_t* desc_midi; // start of whole midi interface descriptor
|
||||
uint16_t desc_midi_total_len;
|
||||
|
||||
const uint8_t* desc_header;
|
||||
const uint8_t* desc_element;
|
||||
const tusb_desc_endpoint_t* desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
|
||||
const tusb_desc_endpoint_t* desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
|
||||
|
||||
uint8_t jack_num;
|
||||
const uint8_t* desc_jack[32]; // list of jack descriptors (embedded + external)
|
||||
} tuh_midi_descriptor_cb_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
uint8_t bInterfaceNumber; // interface number of MIDI streaming
|
||||
uint8_t rx_cable_count;
|
||||
uint8_t tx_cable_count;
|
||||
} tuh_midi_mount_cb_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if MIDI interface is mounted
|
||||
bool tuh_midi_mounted(uint8_t idx);
|
||||
|
||||
// Get Interface index from device address + interface number
|
||||
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||
|
||||
// Get Interface information
|
||||
// return true if index is correct and interface is currently mounted
|
||||
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
|
||||
|
||||
// return the number of virtual midi cables on the device's IN endpoint
|
||||
uint8_t tuh_midi_get_rx_cable_count(uint8_t idx);
|
||||
|
||||
// return the number of virtual midi cables on the device's OUT endpoint
|
||||
uint8_t tuh_midi_get_tx_cable_count(uint8_t idx);
|
||||
|
||||
// return the raw number of bytes available.
|
||||
// Note: this is related but not the same as number of stream bytes available.
|
||||
uint32_t tuh_midi_read_available(uint8_t idx);
|
||||
|
||||
// Send any queued packets to the device if the host hardware is able to do it
|
||||
// Returns the number of bytes flushed to the host hardware or 0 if
|
||||
// the host hardware is busy or there is nothing in queue to send.
|
||||
uint32_t tuh_midi_write_flush(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Packet API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Read all available MIDI packets from the connected device
|
||||
// Return number of bytes read (always multiple of 4)
|
||||
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize);
|
||||
|
||||
// Read a raw MIDI packet from the connected device
|
||||
// Return true if a packet was returned
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_midi_packet_read (uint8_t idx, uint8_t packet[4]) {
|
||||
return 4 == tuh_midi_packet_read_n(idx, packet, 4);
|
||||
}
|
||||
|
||||
// Write all 4-byte packets, data is locally buffered and only transferred when buffered bytes
|
||||
// reach the endpoint packet size or tuh_midi_write_flush() is called
|
||||
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize);
|
||||
|
||||
// Write a 4-bytes packet to the device.
|
||||
// Returns true if the packet was successfully queued.
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
|
||||
return 4 == tuh_midi_packet_write_n(idx, packet, 4);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream API
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUH_MIDI_STREAM_API
|
||||
|
||||
// Queue a message to the device using stream API. data is locally buffered and only transferred when buffered bytes
|
||||
// reach the endpoint packet size or tuh_midi_write_flush() is called
|
||||
// Returns number of bytes was successfully queued.
|
||||
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_buffer, uint32_t bufsize);
|
||||
|
||||
// Get the MIDI stream from the device. Set the value pointed
|
||||
// to by p_cable_num to the MIDI cable number intended to receive it.
|
||||
// The MIDI stream will be stored in the buffer pointed to by p_buffer.
|
||||
// Return the number of bytes added to the buffer.
|
||||
// Note that this function ignores the CIN field of the MIDI packet
|
||||
// because a number of commercial devices out there do not encode
|
||||
// it properly.
|
||||
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize);
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Callbacks (Weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when MIDI interface is detected in enumeration. Application can copy/parse descriptor if needed.
|
||||
// Note: may be fired before tuh_midi_mount_cb(), therefore midi interface is not mounted/ready.
|
||||
void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data);
|
||||
|
||||
// Invoked when device with MIDI interface is mounted.
|
||||
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data);
|
||||
|
||||
// Invoked when device with MIDI interface is un-mounted
|
||||
void tuh_midi_umount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when received new data
|
||||
void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes);
|
||||
|
||||
// Invoked when a TX is complete and therefore space becomes available in TX buffer
|
||||
void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
bool midih_init (void);
|
||||
bool midih_deinit (void);
|
||||
bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool midih_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void midih_close (uint8_t daddr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
401
managed_components/espressif__tinyusb/src/class/msc/msc.h
Normal file
401
managed_components/espressif__tinyusb/src/class/msc/msc.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_MSC_H_
|
||||
#define _TUSB_MSC_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Mass Storage Class Constant
|
||||
//--------------------------------------------------------------------+
|
||||
/// MassStorage Subclass
|
||||
typedef enum
|
||||
{
|
||||
MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D
|
||||
MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device
|
||||
MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device
|
||||
MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device
|
||||
MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device
|
||||
MSC_SUBCLASS_SCSI ///< SCSI transparent command set
|
||||
}msc_subclass_type_t;
|
||||
|
||||
enum {
|
||||
MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian)
|
||||
MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian)
|
||||
};
|
||||
|
||||
/// \brief MassStorage Protocol.
|
||||
/// \details CBI only approved to use with full-speed floppy disk & should not used with highspeed or device other than floppy
|
||||
typedef enum
|
||||
{
|
||||
MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
|
||||
MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt)
|
||||
MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport
|
||||
}msc_protocol_type_t;
|
||||
|
||||
/// MassStorage Class-Specific Control Request
|
||||
typedef enum
|
||||
{
|
||||
MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15
|
||||
MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
|
||||
}msc_request_type_t;
|
||||
|
||||
/// \brief Command Block Status Values
|
||||
/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed
|
||||
/// successfully. A non-zero value shall indicate a failure during command execution according to the following
|
||||
typedef enum
|
||||
{
|
||||
MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED
|
||||
MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED
|
||||
MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR
|
||||
}msc_csw_status_t;
|
||||
|
||||
/// Command Block Wrapper
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
|
||||
uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
|
||||
uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags.
|
||||
uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host.
|
||||
uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero.
|
||||
uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16
|
||||
uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block
|
||||
}msc_cbw_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
|
||||
|
||||
/// Command Status Wrapper
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
|
||||
uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
|
||||
uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResidue the difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
|
||||
uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
|
||||
}msc_csw_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// SCSI Constant
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// SCSI Command Operation Code
|
||||
typedef enum {
|
||||
SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation.
|
||||
SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device.
|
||||
SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters.
|
||||
SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command.
|
||||
SCSI_CMD_START_STOP_UNIT = 0x1B,
|
||||
SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
|
||||
SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device.
|
||||
SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
|
||||
SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
|
||||
SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
|
||||
SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests that the device server transfer the specified logical block(s) from the data-out buffer and write them.
|
||||
}scsi_cmd_type_t;
|
||||
|
||||
/// SCSI Sense Key
|
||||
typedef enum {
|
||||
SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
|
||||
SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< Indicates the last command completed successfully with some recovery action performed by the disc drive.
|
||||
SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
|
||||
SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
|
||||
SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
|
||||
SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters
|
||||
SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset.
|
||||
SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed.
|
||||
SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key.
|
||||
SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
|
||||
SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
|
||||
SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
|
||||
SCSI_SENSE_MISCOMPARE = 0x0e ///< Indicates that the source data did not match the data read from the medium.
|
||||
}scsi_sense_key_type_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
SCSI_PDT_DIRECT_ACCESS = 0x0,
|
||||
SCSI_PDT_SEQUENTIAL_ACCESS = 0x1,
|
||||
SCSI_PDT_PRINTER = 0x2,
|
||||
SCSI_PDT_PROCESSOR = 0x3,
|
||||
SCSI_PDT_WRITE_ONCE = 0x4,
|
||||
SCSI_PDT_CD_DVD = 0x5,
|
||||
SCSI_PDT_SCANNER = 0x6,
|
||||
SCSI_PDT_OPTICAL_DEVICE = 0x7,
|
||||
SCSI_PDT_MEDIUM_CHANGER = 0x8,
|
||||
SCSI_PDT_COMMUNICATIONS = 0x9, // obsolete
|
||||
SCSI_PDT_RAID = 0x0c,
|
||||
SCSI_PDT_ENCLOSURE_SERVICES = 0x0d,
|
||||
SCSI_PDT_SIMPLIFIED_DIRECT_ACCESS = 0x0e,
|
||||
SCSI_PDT_OPTICAL_CARD_READER = 0x0f,
|
||||
SCSI_PDT_BRIDGE = 0x10, ///< Bridge device, e.g. USB to SCSI bridge
|
||||
SCSI_PDT_OBJECT_BASED_STORAGE = 0x11, ///< Object-based storage device
|
||||
SCSI_PDT_AUTOMATION_DRIVE_INTERFACE = 0x12, ///< Automation/Drive Interface (ADI) device
|
||||
} scsi_peripheral_device_type_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// SCSI Primary Command (SPC-4)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// SCSI Test Unit Ready Command
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
|
||||
uint8_t lun ; ///< Logical Unit
|
||||
uint8_t reserved[3] ;
|
||||
uint8_t control ;
|
||||
} scsi_test_unit_ready_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
|
||||
|
||||
/// SCSI Inquiry Command
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
|
||||
uint8_t reserved1 ;
|
||||
uint8_t page_code ;
|
||||
uint8_t reserved2 ;
|
||||
uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
|
||||
uint8_t control ;
|
||||
} scsi_inquiry_t, scsi_request_sense_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
|
||||
|
||||
/// SCSI Inquiry Response Data
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t peripheral_device_type : 5;
|
||||
uint8_t peripheral_qualifier : 3;
|
||||
|
||||
uint8_t : 7;
|
||||
uint8_t is_removable : 1;
|
||||
|
||||
uint8_t version;
|
||||
|
||||
uint8_t response_data_format : 4;
|
||||
uint8_t hierarchical_support : 1;
|
||||
uint8_t normal_aca : 1;
|
||||
uint8_t : 2;
|
||||
|
||||
uint8_t additional_length;
|
||||
|
||||
uint8_t protect : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t third_party_copy : 1;
|
||||
uint8_t target_port_group_support : 2;
|
||||
uint8_t access_control_coordinator : 1;
|
||||
uint8_t scc_support : 1;
|
||||
|
||||
uint8_t addr16 : 1;
|
||||
uint8_t : 3;
|
||||
uint8_t multi_port : 1;
|
||||
uint8_t : 1; // vendor specific
|
||||
uint8_t enclosure_service : 1;
|
||||
uint8_t : 1;
|
||||
|
||||
uint8_t : 1; // vendor specific
|
||||
uint8_t cmd_que : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t sync : 1;
|
||||
uint8_t wbus16 : 1;
|
||||
uint8_t : 2;
|
||||
|
||||
uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product.
|
||||
uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor.
|
||||
uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor.
|
||||
} scsi_inquiry_resp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct");
|
||||
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
|
||||
uint8_t valid : 1;
|
||||
|
||||
uint8_t reserved;
|
||||
|
||||
uint8_t sense_key : 4;
|
||||
uint8_t : 1;
|
||||
uint8_t ili : 1; ///< Incorrect length indicator
|
||||
uint8_t end_of_medium : 1;
|
||||
uint8_t filemark : 1;
|
||||
|
||||
uint32_t information;
|
||||
uint8_t add_sense_len;
|
||||
uint32_t command_specific_info;
|
||||
uint8_t add_sense_code;
|
||||
uint8_t add_sense_qualifier;
|
||||
uint8_t field_replaceable_unit_code;
|
||||
|
||||
uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
|
||||
|
||||
} scsi_sense_fixed_resp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
|
||||
|
||||
uint8_t : 3;
|
||||
uint8_t disable_block_descriptor : 1;
|
||||
uint8_t : 4;
|
||||
|
||||
uint8_t page_code : 6;
|
||||
uint8_t page_control : 2;
|
||||
|
||||
uint8_t subpage_code;
|
||||
uint8_t alloc_length;
|
||||
uint8_t control;
|
||||
} scsi_mode_sense6_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
|
||||
|
||||
// This is only a Mode parameter header(6).
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t data_len;
|
||||
uint8_t medium_type;
|
||||
|
||||
uint8_t reserved : 7;
|
||||
bool write_protected : 1;
|
||||
|
||||
uint8_t block_descriptor_len;
|
||||
} scsi_mode_sense6_resp_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||
uint8_t reserved[3];
|
||||
uint8_t prohibit_removal;
|
||||
uint8_t control;
|
||||
} scsi_prevent_allow_medium_removal_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code;
|
||||
|
||||
uint8_t immded : 1;
|
||||
uint8_t : 7;
|
||||
|
||||
uint8_t TU_RESERVED;
|
||||
|
||||
uint8_t power_condition_mod : 4;
|
||||
uint8_t : 4;
|
||||
|
||||
uint8_t start : 1;
|
||||
uint8_t load_eject : 1;
|
||||
uint8_t no_flush : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t power_condition : 4;
|
||||
|
||||
uint8_t control;
|
||||
} scsi_start_stop_unit_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// SCSI MMC
|
||||
//--------------------------------------------------------------------+
|
||||
/// SCSI Read Format Capacity: Write Capacity
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code;
|
||||
uint8_t reserved[6];
|
||||
uint16_t alloc_length;
|
||||
uint8_t control;
|
||||
} scsi_read_format_capacity_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
uint8_t reserved[3];
|
||||
uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
|
||||
|
||||
uint32_t block_num; /// Number of Logical Blocks
|
||||
uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
|
||||
|
||||
uint8_t reserved2;
|
||||
uint16_t block_size_u16;
|
||||
|
||||
} scsi_read_format_capacity_data_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// SCSI Block Command (SBC-3)
|
||||
// NOTE: All data in SCSI command are in Big Endian
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// SCSI Read Capacity 10 Command: Read Capacity
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
|
||||
uint8_t reserved1 ;
|
||||
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
|
||||
uint16_t reserved2 ;
|
||||
uint8_t partial_medium_indicator ;
|
||||
uint8_t control ;
|
||||
} scsi_read_capacity10_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct");
|
||||
|
||||
/// SCSI Read Capacity 10 Response Data
|
||||
typedef struct {
|
||||
uint32_t last_lba ; ///< The last Logical Block Address of the device
|
||||
uint32_t block_size ; ///< Block size in bytes
|
||||
} scsi_read_capacity10_resp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct");
|
||||
|
||||
/// SCSI Read 10 Command
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t cmd_code ; ///< SCSI OpCode
|
||||
uint8_t reserved ; // has LUN according to wiki
|
||||
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
|
||||
uint8_t reserved2 ;
|
||||
uint16_t block_count ; ///< Number of Blocks used by this command
|
||||
uint8_t control ;
|
||||
} scsi_read10_t, scsi_write10_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
|
||||
TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_MSC_H_ */
|
||||
957
managed_components/espressif__tinyusb/src/class/msc/msc_device.c
Normal file
957
managed_components/espressif__tinyusb/src/class/msc/msc_device.c
Normal file
@@ -0,0 +1,957 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_MSC)
|
||||
|
||||
#include "device/dcd.h" // for faking dcd_event_xfer_complete
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "msc_device.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_MSC_LOG_LEVEL
|
||||
#define CFG_TUD_MSC_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MSC_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
(void) lun; (void) vendor_id; (void) product_id; (void) product_rev;
|
||||
}
|
||||
TU_ATTR_WEAK uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uint32_t bufsize) {
|
||||
(void) lun; (void) inquiry_resp; (void) bufsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
MSC_STAGE_CMD = 0,
|
||||
MSC_STAGE_DATA,
|
||||
MSC_STAGE_STATUS,
|
||||
MSC_STAGE_STATUS_SENT,
|
||||
MSC_STAGE_NEED_RESET,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
TU_ATTR_ALIGNED(4) msc_cbw_t cbw; // 31 bytes
|
||||
uint8_t rhport;
|
||||
|
||||
TU_ATTR_ALIGNED(4) msc_csw_t csw; // 13 bytes
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
|
||||
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
|
||||
|
||||
// Bulk Only Transfer (BOT) Protocol
|
||||
uint8_t stage;
|
||||
|
||||
// SCSI Sense Response Data
|
||||
uint8_t sense_key;
|
||||
uint8_t add_sense_code;
|
||||
uint8_t add_sense_qualifier;
|
||||
|
||||
uint8_t pending_io; // pending async IO
|
||||
}mscd_interface_t;
|
||||
|
||||
static mscd_interface_t _mscd_itf;
|
||||
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(buf, CFG_TUD_MSC_EP_BUFSIZE);
|
||||
} _mscd_epbuf;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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(mscd_interface_t* p_msc);
|
||||
static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes);
|
||||
static void proc_write10_cmd(mscd_interface_t* p_msc);
|
||||
static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes);
|
||||
static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes);
|
||||
static bool proc_stage_status(mscd_interface_t* p_msc);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) {
|
||||
return tu_bit_test(dir, 7);
|
||||
}
|
||||
|
||||
static inline bool send_csw(mscd_interface_t* p_msc) {
|
||||
// Data residue is always = host expect - actual transferred
|
||||
uint8_t rhport = p_msc->rhport;
|
||||
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
|
||||
p_msc->stage = MSC_STAGE_STATUS_SENT;
|
||||
memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t));
|
||||
return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t));
|
||||
}
|
||||
|
||||
static inline bool prepare_cbw(mscd_interface_t* p_msc) {
|
||||
uint8_t rhport = p_msc->rhport;
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t));
|
||||
}
|
||||
|
||||
static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) {
|
||||
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||
msc_csw_t * p_csw = &p_msc->csw;
|
||||
uint8_t rhport = p_msc->rhport;
|
||||
|
||||
p_csw->status = status;
|
||||
p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
|
||||
// failed but sense key is not set: default to Illegal Request
|
||||
if (p_msc->sense_key == 0) {
|
||||
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
}
|
||||
|
||||
// If there is data stage and not yet complete, stall it
|
||||
if (p_cbw->total_bytes && p_csw->data_residue) {
|
||||
if (is_data_in(p_cbw->dir)) {
|
||||
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||
} else {
|
||||
usbd_edpt_stall(rhport, p_msc->ep_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
|
||||
// use offsetof to avoid pointer to the odd/unaligned address
|
||||
const uint32_t lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
|
||||
return tu_ntohl(lba); // lba is in Big Endian
|
||||
}
|
||||
|
||||
static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
|
||||
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
|
||||
return tu_ntohs(block_count);
|
||||
}
|
||||
|
||||
static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
|
||||
// first extract block count in the command
|
||||
uint16_t const block_count = rdwr10_get_blockcount(cbw);
|
||||
if (block_count == 0) {
|
||||
return 0; // invalid block count
|
||||
}
|
||||
return (uint16_t) (cbw->total_bytes / block_count);
|
||||
}
|
||||
|
||||
static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
|
||||
uint8_t status = MSC_CSW_STATUS_PASSED;
|
||||
uint16_t const block_count = rdwr10_get_blockcount(cbw);
|
||||
|
||||
if (cbw->total_bytes == 0) {
|
||||
if (block_count) {
|
||||
TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
|
||||
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||
} else {
|
||||
// no data transfer, only exist in complaint test suite
|
||||
}
|
||||
} else {
|
||||
if (SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir)) {
|
||||
TU_LOG_DRV(" SCSI case 10 (Ho <> Di)\r\n");
|
||||
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||
} else if (SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir)) {
|
||||
TU_LOG_DRV(" SCSI case 8 (Hi <> Do)\r\n");
|
||||
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||
} else if (0 == block_count) {
|
||||
TU_LOG_DRV(" SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
|
||||
status = MSC_CSW_STATUS_FAILED;
|
||||
} else if (cbw->total_bytes / block_count == 0) {
|
||||
TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
|
||||
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool proc_stage_status(mscd_interface_t *p_msc) {
|
||||
uint8_t rhport = p_msc->rhport;
|
||||
msc_cbw_t const *p_cbw = &p_msc->cbw;
|
||||
|
||||
// skip status if epin is currently stalled, will do it when received Clear Stall request
|
||||
if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) {
|
||||
if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) {
|
||||
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
|
||||
// TU_LOG_DRV(" SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
|
||||
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||
} else {
|
||||
TU_ASSERT(send_csw(p_msc));
|
||||
}
|
||||
}
|
||||
|
||||
#if TU_CHECK_MCU(OPT_MCU_CXD56)
|
||||
// WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
|
||||
// There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
|
||||
// hope everything will work
|
||||
if (usbd_edpt_stalled(rhport, p_msc->ep_in)) {
|
||||
usbd_edpt_clear_stall(rhport, p_msc->ep_in);
|
||||
send_csw(p_msc);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]) {
|
||||
(void) lun;
|
||||
(void) scsi_cmd;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
(void) start;
|
||||
(void) load_eject;
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control) {
|
||||
(void) lun;
|
||||
(void) prohibit_removal;
|
||||
(void) control;
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize) {
|
||||
(void) lun;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return sizeof(scsi_sense_fixed_resp_t);
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
|
||||
|
||||
TU_ATTR_UNUSED tu_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" },
|
||||
{ .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
|
||||
{ .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
|
||||
{ .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
|
||||
{ .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent/Allow Medium Removal" },
|
||||
{ .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
|
||||
{ .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
|
||||
{ .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
|
||||
{ .key = SCSI_CMD_READ_10 , .data = "Read10" },
|
||||
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
|
||||
};
|
||||
|
||||
TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table = {
|
||||
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
|
||||
.items = _msc_scsi_cmd_lookup
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) {
|
||||
(void) lun;
|
||||
_mscd_itf.sense_key = sense_key;
|
||||
_mscd_itf.add_sense_code = add_sense_code;
|
||||
_mscd_itf.add_sense_qualifier = add_sense_qualifier;
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void set_sense_medium_not_present(uint8_t lun) {
|
||||
// default sense is NOT READY, MEDIUM NOT PRESENT
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
|
||||
}
|
||||
|
||||
static void proc_async_io_done(void *bytes_io) {
|
||||
mscd_interface_t *p_msc = &_mscd_itf;
|
||||
TU_VERIFY(p_msc->pending_io, );
|
||||
const int32_t nbytes = (int32_t) (intptr_t) bytes_io;
|
||||
const uint8_t cmd = p_msc->cbw.command[0];
|
||||
|
||||
p_msc->pending_io = 0;
|
||||
switch (cmd) {
|
||||
case SCSI_CMD_READ_10:
|
||||
proc_read_io_data(p_msc, nbytes);
|
||||
break;
|
||||
|
||||
case SCSI_CMD_WRITE_10:
|
||||
proc_write_io_data(p_msc, (uint32_t) nbytes, nbytes);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
// send status if stage is transitioned to STATUS
|
||||
if (p_msc->stage == MSC_STAGE_STATUS) {
|
||||
proc_stage_status(p_msc);
|
||||
}
|
||||
}
|
||||
|
||||
bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr) {
|
||||
// Precheck to avoid queueing multiple RW done callback
|
||||
TU_VERIFY(_mscd_itf.pending_io);
|
||||
if (bytes_io == 0) {
|
||||
bytes_io = TUD_MSC_RET_ERROR; // 0 is treated as error, no reason to call this with BUSY here
|
||||
}
|
||||
usbd_defer_func(proc_async_io_done, (void *) (intptr_t) bytes_io, in_isr);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init(void) {
|
||||
TU_LOG_INT(CFG_TUD_MSC_LOG_LEVEL, sizeof(mscd_interface_t));
|
||||
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||
}
|
||||
|
||||
bool mscd_deinit(void) {
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
void mscd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||
}
|
||||
|
||||
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_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
|
||||
MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
|
||||
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||
TU_ASSERT(max_len >= drv_len, 0); // Max length must be at least 1 interface + 2 endpoints
|
||||
|
||||
mscd_interface_t * p_msc = &_mscd_itf;
|
||||
p_msc->itf_num = itf_desc->bInterfaceNumber;
|
||||
p_msc->rhport = rhport;
|
||||
|
||||
// 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(prepare_cbw(p_msc), drv_len);
|
||||
|
||||
return drv_len;
|
||||
}
|
||||
|
||||
static void proc_bot_reset(mscd_interface_t* p_msc) {
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
p_msc->total_len = 0;
|
||||
p_msc->xferred_len = 0;
|
||||
p_msc->sense_key = 0;
|
||||
p_msc->add_sense_code = 0;
|
||||
p_msc->add_sense_qualifier = 0;
|
||||
}
|
||||
|
||||
// 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_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true; // nothing to do with DATA & ACK stage
|
||||
}
|
||||
|
||||
mscd_interface_t* p_msc = &_mscd_itf;
|
||||
|
||||
// Clear Endpoint Feature (stall) for recovery
|
||||
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
|
||||
TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
|
||||
TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
|
||||
TUSB_REQ_FEATURE_EDPT_HALT == request->wValue ) {
|
||||
uint8_t const ep_addr = tu_u16_low(request->wIndex);
|
||||
|
||||
if (p_msc->stage == MSC_STAGE_NEED_RESET) {
|
||||
// reset recovery is required to recover from this stage
|
||||
// Clear Stall request cannot resolve this -> continue to stall endpoint
|
||||
usbd_edpt_stall(rhport, ep_addr);
|
||||
} else {
|
||||
if (ep_addr == p_msc->ep_in) {
|
||||
if (p_msc->stage == MSC_STAGE_STATUS) {
|
||||
// resume sending SCSI status if we are in this stage previously before stalled
|
||||
TU_ASSERT(send_csw(p_msc));
|
||||
}
|
||||
} else if (ep_addr == p_msc->ep_out) {
|
||||
if (p_msc->stage == MSC_STAGE_CMD) {
|
||||
// part of reset recovery (probably due to invalid CBW) -> prepare for new command
|
||||
// Note: skip if already queued previously
|
||||
if (usbd_edpt_ready(rhport, p_msc->ep_out)) {
|
||||
TU_ASSERT(prepare_cbw(p_msc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// From this point only handle class request only
|
||||
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||
|
||||
switch ( request->bRequest ) {
|
||||
case MSC_REQ_RESET:
|
||||
TU_LOG_DRV(" MSC BOT Reset\r\n");
|
||||
TU_VERIFY(request->wValue == 0 && request->wLength == 0);
|
||||
proc_bot_reset(p_msc); // driver state reset
|
||||
tud_control_status(rhport, request);
|
||||
break;
|
||||
|
||||
case MSC_REQ_GET_MAX_LUN: {
|
||||
TU_LOG_DRV(" MSC Get Max Lun\r\n");
|
||||
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
|
||||
|
||||
uint8_t maxlun = tud_msc_get_maxlun_cb();
|
||||
TU_VERIFY(maxlun);
|
||||
maxlun--; // MAX LUN is minus 1 by specs
|
||||
tud_control_xfer(rhport, request, &maxlun, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
default: return false; // stall unsupported request
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
(void) event;
|
||||
|
||||
mscd_interface_t* p_msc = &_mscd_itf;
|
||||
msc_cbw_t * p_cbw = &p_msc->cbw;
|
||||
msc_csw_t * p_csw = &p_msc->csw;
|
||||
|
||||
switch (p_msc->stage) {
|
||||
case MSC_STAGE_CMD: {
|
||||
//------------- new CBW received -------------//
|
||||
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
|
||||
if (ep_addr != p_msc->ep_out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint32_t signature = tu_le32toh(tu_unaligned_read32(_mscd_epbuf.buf));
|
||||
|
||||
if (!(xferred_bytes == sizeof(msc_cbw_t) && signature == MSC_CBW_SIGNATURE)) {
|
||||
// BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
|
||||
TU_LOG_DRV(" SCSI CBW is not valid\r\n");
|
||||
p_msc->stage = MSC_STAGE_NEED_RESET;
|
||||
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||
usbd_edpt_stall(rhport, p_msc->ep_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(p_cbw, _mscd_epbuf.buf, sizeof(msc_cbw_t));
|
||||
|
||||
TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_cbw, xferred_bytes, 2);
|
||||
|
||||
p_csw->signature = MSC_CSW_SIGNATURE;
|
||||
p_csw->tag = p_cbw->tag;
|
||||
p_csw->data_residue = 0;
|
||||
p_csw->status = MSC_CSW_STATUS_PASSED;
|
||||
|
||||
/*------------- Parse command and prepare DATA -------------*/
|
||||
p_msc->stage = MSC_STAGE_DATA;
|
||||
p_msc->total_len = p_cbw->total_bytes;
|
||||
p_msc->xferred_len = 0;
|
||||
|
||||
// Read10 or Write10
|
||||
if ((SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0])) {
|
||||
uint8_t const status = rdwr10_validate_cmd(p_cbw);
|
||||
|
||||
if (status != MSC_CSW_STATUS_PASSED) {
|
||||
fail_scsi_op(p_msc, status);
|
||||
} else if (p_cbw->total_bytes) {
|
||||
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
|
||||
proc_read10_cmd(p_msc);
|
||||
} else {
|
||||
proc_write10_cmd(p_msc);
|
||||
}
|
||||
} else {
|
||||
// no data transfer, only exist in complaint test suite
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
}
|
||||
} else {
|
||||
// For other SCSI commands
|
||||
// 1. OUT : queue transfer (invoke app callback after done)
|
||||
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
|
||||
if ((p_cbw->total_bytes > 0) && !is_data_in(p_cbw->dir)) {
|
||||
if (p_cbw->total_bytes > CFG_TUD_MSC_EP_BUFSIZE) {
|
||||
TU_LOG_DRV(" SCSI reject non READ10/WRITE10 with large data\r\n");
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
|
||||
// but it is OK to just receive data then responded with failed status
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
|
||||
}
|
||||
} else {
|
||||
// First process if it is a built-in commands
|
||||
int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, CFG_TUD_MSC_EP_BUFSIZE);
|
||||
|
||||
// Invoke user callback if not built-in
|
||||
if ((resplen < 0) && (p_msc->sense_key == 0)) {
|
||||
resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t)p_msc->total_len);
|
||||
}
|
||||
|
||||
if (resplen < 0) {
|
||||
// unsupported command
|
||||
TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else if (resplen == 0) {
|
||||
if (p_cbw->total_bytes) {
|
||||
// 6.7 The 13 Cases: case 4 (Hi > Dn)
|
||||
// TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// case 1 Hn = Dn: all good
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
}
|
||||
} else {
|
||||
if (p_cbw->total_bytes == 0) {
|
||||
// 6.7 The 13 Cases: case 2 (Hn < Di)
|
||||
// TU_LOG_DRV(" SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
} else {
|
||||
// cannot return more than host expect
|
||||
p_msc->total_len = tu_min32((uint32_t)resplen, p_cbw->total_bytes);
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSC_STAGE_DATA:
|
||||
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
|
||||
TU_ASSERT(xferred_bytes <= CFG_TUD_MSC_EP_BUFSIZE); // sanity check to avoid buffer overflow
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, _mscd_epbuf.buf, xferred_bytes, 2);
|
||||
|
||||
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
|
||||
p_msc->xferred_len += xferred_bytes;
|
||||
|
||||
if ( p_msc->xferred_len >= p_msc->total_len ) {
|
||||
// Data Stage is complete
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
}else {
|
||||
proc_read10_cmd(p_msc);
|
||||
}
|
||||
} else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) {
|
||||
proc_write10_host_data(p_msc, xferred_bytes);
|
||||
} else {
|
||||
p_msc->xferred_len += xferred_bytes;
|
||||
|
||||
// OUT transfer, invoke callback if needed
|
||||
if ( !is_data_in(p_cbw->dir) ) {
|
||||
int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t) p_msc->total_len);
|
||||
|
||||
if ( cb_result < 0 ) {
|
||||
// unsupported command
|
||||
TU_LOG_DRV(" SCSI unsupported command\r\n");
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
}else {
|
||||
// TODO haven't implement this scenario any further yet
|
||||
}
|
||||
}
|
||||
|
||||
if ( p_msc->xferred_len >= p_msc->total_len ) {
|
||||
// Data Stage is complete
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
} else {
|
||||
// This scenario with command that take more than one transfer is already rejected at Command stage
|
||||
TU_BREAKPOINT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_STAGE_STATUS:
|
||||
// processed immediately after this switch, supposedly to be empty
|
||||
break;
|
||||
|
||||
case MSC_STAGE_STATUS_SENT:
|
||||
// Status phase is complete
|
||||
if ((ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t))) {
|
||||
TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
|
||||
// TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, 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:
|
||||
tud_msc_read10_complete_cb(p_cbw->lun);
|
||||
break;
|
||||
|
||||
case SCSI_CMD_WRITE_10:
|
||||
tud_msc_write10_complete_cb(p_cbw->lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
|
||||
break;
|
||||
}
|
||||
|
||||
TU_ASSERT(prepare_cbw(p_msc));
|
||||
} else {
|
||||
// Any xfer ended here is considered unknown error, ignore it
|
||||
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (p_msc->stage == MSC_STAGE_STATUS) {
|
||||
TU_ASSERT(proc_stage_status(p_msc));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
mscd_interface_t* p_msc = &_mscd_itf;
|
||||
|
||||
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;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if (p_msc->sense_key == 0) {
|
||||
set_sense_medium_not_present(lun);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SCSI_CMD_START_STOP_UNIT:
|
||||
resplen = 0;
|
||||
|
||||
scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd;
|
||||
if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) {
|
||||
// Failed status response
|
||||
resplen = -1;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if (p_msc->sense_key == 0) {
|
||||
set_sense_medium_not_present(lun);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
resplen = 0;
|
||||
|
||||
scsi_prevent_allow_medium_removal_t const* prevent_allow = (scsi_prevent_allow_medium_removal_t const*)scsi_cmd;
|
||||
if (!tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control)) {
|
||||
// Failed status response
|
||||
resplen = -1;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if (p_msc->sense_key == 0) {
|
||||
set_sense_medium_not_present(lun);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if (p_msc->sense_key == 0) {
|
||||
set_sense_medium_not_present(lun);
|
||||
}
|
||||
} 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);
|
||||
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) 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;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if (p_msc->sense_key == 0) {
|
||||
set_sense_medium_not_present(lun);
|
||||
}
|
||||
} 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);
|
||||
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SCSI_CMD_INQUIRY: {
|
||||
scsi_inquiry_resp_t *inquiry_rsp = (scsi_inquiry_resp_t *) buffer;
|
||||
tu_memclr(inquiry_rsp, sizeof(scsi_inquiry_resp_t));
|
||||
inquiry_rsp->is_removable = 1;
|
||||
inquiry_rsp->version = 2;
|
||||
inquiry_rsp->response_data_format = 2;
|
||||
inquiry_rsp->additional_length = sizeof(scsi_inquiry_resp_t) - 5;
|
||||
|
||||
resplen = (int32_t) tud_msc_inquiry2_cb(lun, inquiry_rsp, bufsize);
|
||||
if (resplen == 0) {
|
||||
// stub callback with no response, use v1 callback
|
||||
tud_msc_inquiry_cb(lun, inquiry_rsp->vendor_id, inquiry_rsp->product_id, inquiry_rsp->product_rev);
|
||||
resplen = sizeof(scsi_inquiry_resp_t);
|
||||
}
|
||||
}
|
||||
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 = tud_msc_is_writable_cb(lun);
|
||||
|
||||
mode_resp.write_protected = !writable;
|
||||
|
||||
resplen = sizeof(mode_resp);
|
||||
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &mode_resp, (size_t) resplen));
|
||||
}
|
||||
break;
|
||||
|
||||
case SCSI_CMD_REQUEST_SENSE: {
|
||||
scsi_sense_fixed_resp_t sense_rsp = {
|
||||
.response_code = 0x70, // current, fixed format
|
||||
.valid = 1
|
||||
};
|
||||
|
||||
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
|
||||
sense_rsp.sense_key = (uint8_t)(p_msc->sense_key & 0x0F);
|
||||
sense_rsp.add_sense_code = p_msc->add_sense_code;
|
||||
sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
|
||||
|
||||
resplen = sizeof(sense_rsp);
|
||||
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen));
|
||||
|
||||
// request sense callback could overwrite the sense data
|
||||
resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
|
||||
|
||||
// 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(mscd_interface_t* p_msc) {
|
||||
msc_cbw_t const* p_cbw = &p_msc->cbw;
|
||||
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
|
||||
// Adjust lba & offset with transferred bytes
|
||||
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
|
||||
uint32_t const offset = p_msc->xferred_len % block_sz;
|
||||
|
||||
// remaining bytes capped at class buffer
|
||||
int32_t nbytes = (int32_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
|
||||
|
||||
p_msc->pending_io = 1;
|
||||
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes);
|
||||
if (nbytes != TUD_MSC_RET_ASYNC) {
|
||||
p_msc->pending_io = 0;
|
||||
proc_read_io_data(p_msc, nbytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) {
|
||||
const uint8_t rhport = p_msc->rhport;
|
||||
if (nbytes > 0) {
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
|
||||
} else {
|
||||
// nbytes is status
|
||||
switch (nbytes) {
|
||||
case TUD_MSC_RET_ERROR:
|
||||
// error -> endpoint is stalled & status in CSW set to failed
|
||||
TU_LOG_DRV(" IO read() failed\r\n");
|
||||
set_sense_medium_not_present(p_msc->cbw.lun);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
break;
|
||||
|
||||
case TUD_MSC_RET_BUSY:
|
||||
// not ready yet -> fake a transfer complete so that this driver callback will fire again
|
||||
dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_write10_cmd(mscd_interface_t* p_msc) {
|
||||
msc_cbw_t const* p_cbw = &p_msc->cbw;
|
||||
bool writable = tud_msc_is_writable_cb(p_cbw->lun);
|
||||
|
||||
if (!writable) {
|
||||
// Not writable, complete this SCSI op with error
|
||||
// Sense = Write protected
|
||||
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
// remaining bytes capped at class buffer
|
||||
uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
|
||||
// Write10 callback will be called later when usb transfer complete
|
||||
TU_ASSERT(usbd_edpt_xfer(p_msc->rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
|
||||
}
|
||||
|
||||
// process new data arrived from WRITE10
|
||||
static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) {
|
||||
msc_cbw_t const* p_cbw = &p_msc->cbw;
|
||||
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
|
||||
|
||||
// Adjust lba & offset with transferred bytes
|
||||
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
|
||||
uint32_t const offset = p_msc->xferred_len % block_sz;
|
||||
|
||||
p_msc->pending_io = 1;
|
||||
int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
|
||||
if (nbytes != TUD_MSC_RET_ASYNC) {
|
||||
p_msc->pending_io = 0;
|
||||
proc_write_io_data(p_msc, xferred_bytes, nbytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) {
|
||||
if (nbytes < 0) {
|
||||
// nbytes is status
|
||||
switch (nbytes) {
|
||||
case TUD_MSC_RET_ERROR:
|
||||
// IO error -> failed this scsi op
|
||||
TU_LOG_DRV(" IO write() failed\r\n");
|
||||
set_sense_medium_not_present(p_msc->cbw.lun);
|
||||
fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
if ((uint32_t)nbytes < xferred_bytes) {
|
||||
// Application consume less than what we got including TUD_MSC_RET_BUSY (0)
|
||||
const uint32_t left_over = xferred_bytes - (uint32_t)nbytes;
|
||||
if (nbytes > 0) {
|
||||
memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
|
||||
}
|
||||
|
||||
// fake a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameters
|
||||
dcd_event_xfer_complete(p_msc->rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
|
||||
} else {
|
||||
// Application consume all bytes in our buffer
|
||||
p_msc->xferred_len += xferred_bytes;
|
||||
|
||||
if (p_msc->xferred_len >= p_msc->total_len) {
|
||||
// Data Stage is complete
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
} else {
|
||||
// prepare to receive more data from host
|
||||
proc_write10_cmd(p_msc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
170
managed_components/espressif__tinyusb/src/class/msc/msc_device.h
Normal file
170
managed_components/espressif__tinyusb/src/class/msc/msc_device.h
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_MSC_DEVICE_H_
|
||||
#define _TUSB_MSC_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "msc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#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
|
||||
|
||||
#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
|
||||
|
||||
// Return value of callback functions
|
||||
enum {
|
||||
TUD_MSC_RET_BUSY = 0, // Busy, e.g disk I/O is not ready
|
||||
TUD_MSC_RET_ERROR = -1,
|
||||
TUD_MSC_RET_ASYNC = -2, // Asynchronous IO
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
// Called by Application once asynchronous I/O operation is done
|
||||
// bytes_io is number of bytes in I/O op, typically the bufsize in read/write_cb() or
|
||||
// TUD_MSC_RET_ERROR (-1) for error. Note TUD_MSC_RET_BUSY (0) will be treated as error as well.
|
||||
bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callbacks (WEAK is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/*
|
||||
Invoked when received SCSI READ10/WRITE10 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 bytes read or status.
|
||||
- 0 < ret < bufsize: These bytes are transferred first and callback will be invoked again for remaining data.
|
||||
- TUD_MSC_RET_BUSY
|
||||
Application is buys e.g disk I/O not ready. Callback will be invoked again with the same parameters later on.
|
||||
- TUD_MSC_RET_ERROR
|
||||
error such as invalid address. This request will be STALLed and scsi command will be failed
|
||||
- TUD_MSC_RET_ASYNC
|
||||
Data I/O will be done asynchronously in a background task. Application should return immediately.
|
||||
tud_msc_async_io_done() must be called once IO/ is done to signal completion.
|
||||
*/
|
||||
int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
|
||||
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, v1, application should use v2 if possible
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY, v2 with full inquiry response
|
||||
// Some inquiry_resp's fields are already filled with default values, application can update them
|
||||
// Return length of inquiry response, typically sizeof(scsi_inquiry_resp_t) (36 bytes), can be longer if included vendor data.
|
||||
uint32_t tud_msc_inquiry2_cb(uint8_t lun, scsi_inquiry_resp_t *inquiry_resp, uint32_t bufsize);
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun);
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
|
||||
|
||||
/**
|
||||
* Invoked when received an SCSI command not in built-in list below.
|
||||
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
|
||||
* - READ10 and WRITE10 has their own callbacks
|
||||
*
|
||||
* \param[in] lun Logical unit number
|
||||
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
|
||||
* \param[out] buffer Buffer for SCSI Data Stage.
|
||||
* - For INPUT: application must fill this with response.
|
||||
* - For OUTPUT it holds the Data from host
|
||||
* \param[in] bufsize Buffer's length.
|
||||
*
|
||||
* \return Actual bytes processed, can be zero for no-data command.
|
||||
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
|
||||
* endpoint and return failed status in command status wrapper phase.
|
||||
*/
|
||||
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
|
||||
|
||||
/*------------- Optional callbacks -------------*/
|
||||
|
||||
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
|
||||
uint8_t tud_msc_get_maxlun_cb(void);
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
|
||||
|
||||
//Invoked when we receive the Prevent / Allow Medium Removal command
|
||||
bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
|
||||
|
||||
// Invoked when received REQUEST_SENSE
|
||||
int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
|
||||
|
||||
// Invoked when Read10 command is complete
|
||||
void tud_msc_read10_complete_cb(uint8_t lun);
|
||||
|
||||
// Invoke when Write10 command is complete, can be used to flush flash caching
|
||||
void tud_msc_write10_complete_cb(uint8_t lun);
|
||||
|
||||
// Invoked when command in tud_msc_scsi_cb is complete
|
||||
void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
|
||||
|
||||
// Invoked to check if device is writable as part of SCSI WRITE10
|
||||
bool tud_msc_is_writable_cb(uint8_t lun);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init (void);
|
||||
bool mscd_deinit (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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_MSC_DEVICE_H_ */
|
||||
517
managed_components/espressif__tinyusb/src/class/msc/msc_host.c
Normal file
517
managed_components/espressif__tinyusb/src/class/msc/msc_host.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUH_ENABLED && CFG_TUH_MSC
|
||||
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
|
||||
#include "msc_host.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUH_MSC_LOG_LEVEL
|
||||
#define CFG_TUH_MSC_LOG_LEVEL CFG_TUH_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MSC_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
MSC_STAGE_IDLE = 0,
|
||||
MSC_STAGE_CMD,
|
||||
MSC_STAGE_DATA,
|
||||
MSC_STAGE_STATUS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
uint8_t max_lun;
|
||||
|
||||
volatile bool configured; // Receive SET_CONFIGURE
|
||||
volatile bool mounted; // Enumeration is complete
|
||||
|
||||
// SCSI command data
|
||||
uint8_t stage;
|
||||
void* buffer;
|
||||
tuh_msc_complete_cb_t complete_cb;
|
||||
uintptr_t complete_arg;
|
||||
|
||||
struct {
|
||||
uint32_t block_size;
|
||||
uint32_t block_count;
|
||||
} capacity[CFG_TUH_MSC_MAXLUN];
|
||||
} msch_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUH_EPBUF_TYPE_DEF(msc_cbw_t, cbw);
|
||||
TUH_EPBUF_TYPE_DEF(msc_csw_t, csw);
|
||||
} msch_epbuf_t;
|
||||
|
||||
static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
|
||||
CFG_TUH_MEM_SECTION static msch_epbuf_t _msch_epbuf[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline msch_interface_t* get_itf(uint8_t daddr) {
|
||||
return &_msch_itf[daddr - 1];
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline msch_epbuf_t* get_epbuf(uint8_t daddr) {
|
||||
return &_msch_epbuf[daddr - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr) {
|
||||
(void) dev_addr;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr) {
|
||||
(void) dev_addr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->max_lun;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_size;
|
||||
}
|
||||
|
||||
bool tuh_msc_mounted(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted;
|
||||
}
|
||||
|
||||
bool tuh_msc_ready(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API: SCSI COMMAND
|
||||
//--------------------------------------------------------------------+
|
||||
static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
|
||||
tu_memclr(cbw, sizeof(msc_cbw_t));
|
||||
cbw->signature = MSC_CBW_SIGNATURE;
|
||||
cbw->tag = 0x54555342; // TUSB
|
||||
cbw->lun = lun;
|
||||
}
|
||||
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
|
||||
msch_epbuf_t* epbuf = get_epbuf(daddr);
|
||||
|
||||
epbuf->cbw = *cbw;
|
||||
p_msc->buffer = data;
|
||||
p_msc->complete_cb = complete_cb;
|
||||
p_msc->complete_arg = arg;
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
|
||||
if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &epbuf->cbw, sizeof(msc_cbw_t))) {
|
||||
usbh_edpt_release(daddr, p_msc->ep_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
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 tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
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 = 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_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, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
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 tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = 18; // TODO sense response
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_request_sense_t);
|
||||
|
||||
scsi_request_sense_t const cmd_request_sense = {
|
||||
.cmd_code = SCSI_CMD_REQUEST_SENSE,
|
||||
.alloc_length = 18
|
||||
};
|
||||
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
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, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
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_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_write10_t);
|
||||
|
||||
scsi_write10_t const cmd_write10 = {
|
||||
.cmd_code = SCSI_CMD_WRITE_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
};
|
||||
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// MSC interface Reset (not used now)
|
||||
bool tuh_msc_reset(uint8_t dev_addr) {
|
||||
tusb_control_request_t const new_request = {
|
||||
.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
|
||||
//--------------------------------------------------------------------+
|
||||
bool msch_init(void) {
|
||||
TU_LOG_DRV("sizeof(msch_interface_t) = %u\r\n", sizeof(msch_interface_t));
|
||||
TU_LOG_DRV("sizeof(msch_epbuf_t) = %u\r\n", sizeof(msch_epbuf_t));
|
||||
tu_memclr(_msch_itf, sizeof(_msch_itf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool msch_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void msch_close(uint8_t dev_addr) {
|
||||
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured,);
|
||||
|
||||
TU_LOG_DRV(" MSCh close addr = %d\r\n", dev_addr);
|
||||
|
||||
// invoke Application Callback
|
||||
if (p_msc->mounted) {
|
||||
tuh_msc_umount_cb(dev_addr);
|
||||
}
|
||||
|
||||
tu_memclr(p_msc, sizeof(msch_interface_t));
|
||||
}
|
||||
|
||||
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
msch_epbuf_t* epbuf = get_epbuf(dev_addr);
|
||||
msc_cbw_t const * cbw = &epbuf->cbw;
|
||||
msc_csw_t * csw = &epbuf->csw;
|
||||
|
||||
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));
|
||||
if (cbw->total_bytes && p_msc->buffer) {
|
||||
// Data stage if any
|
||||
p_msc->stage = MSC_STAGE_DATA;
|
||||
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, (uint16_t) cbw->total_bytes));
|
||||
break;
|
||||
}
|
||||
|
||||
TU_ATTR_FALLTHROUGH; // fallthrough to status stage
|
||||
|
||||
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*) csw, (uint16_t) sizeof(msc_csw_t)));
|
||||
break;
|
||||
|
||||
case MSC_STAGE_STATUS:
|
||||
// SCSI op is complete
|
||||
p_msc->stage = MSC_STAGE_IDLE;
|
||||
|
||||
if (p_msc->complete_cb) {
|
||||
tuh_msc_complete_data_t const cb_data = {
|
||||
.cbw = cbw,
|
||||
.csw = csw,
|
||||
.scsi_data = p_msc->buffer,
|
||||
.user_arg = p_msc->complete_arg
|
||||
};
|
||||
p_msc->complete_cb(dev_addr, &cb_data);
|
||||
}
|
||||
break;
|
||||
|
||||
// unknown state
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MSC Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer);
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
|
||||
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
|
||||
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||
|
||||
// msc driver length is fixed
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(drv_len <= max_len);
|
||||
|
||||
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 && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
|
||||
|
||||
if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
|
||||
p_msc->ep_in = ep_desc->bEndpointAddress;
|
||||
} else {
|
||||
p_msc->ep_out = ep_desc->bEndpointAddress;
|
||||
}
|
||||
|
||||
ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
|
||||
}
|
||||
|
||||
p_msc->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool msch_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
TU_ASSERT(p_msc->itf_num == itf_num);
|
||||
p_msc->configured = true;
|
||||
|
||||
//------------- Get Max Lun -------------//
|
||||
TU_LOG_DRV("MSC Get Max Lun\r\n");
|
||||
tusb_control_request_t const 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 = itf_num,
|
||||
.wLength = 1
|
||||
};
|
||||
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = enum_buf,
|
||||
.complete_cb = config_get_maxlun_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
|
||||
// MAXLUN's response is minus 1 by specs, STALL means 1
|
||||
if (XFER_RESULT_SUCCESS == xfer->result) {
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
p_msc->max_lun = enum_buf[0] + 1;
|
||||
} else {
|
||||
p_msc->max_lun = 1;
|
||||
}
|
||||
|
||||
TU_LOG_DRV(" Max LUN = %u\r\n", p_msc->max_lun);
|
||||
|
||||
// TODO multiple LUN support
|
||||
TU_LOG_DRV("SCSI Test Unit Ready\r\n");
|
||||
uint8_t const lun = 0;
|
||||
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
|
||||
}
|
||||
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
|
||||
if (csw->status == 0) {
|
||||
// Unit is ready, read its capacity
|
||||
TU_LOG_DRV("SCSI Read Capacity\r\n");
|
||||
tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf,
|
||||
config_read_capacity_complete, 0);
|
||||
} 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_LOG_DRV("SCSI Request Sense\r\n");
|
||||
TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, enum_buf, config_request_sense_complete, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
TU_ASSERT(csw->status == 0);
|
||||
TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
TU_ASSERT(csw->status == 0);
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
uint8_t* enum_buf = usbh_get_enum_buf();
|
||||
|
||||
// Capacity response field: Block size and Last LBA are both Big-Endian
|
||||
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf;
|
||||
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
|
||||
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
|
||||
|
||||
// Mark enumeration is complete
|
||||
p_msc->mounted = true;
|
||||
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
|
||||
132
managed_components/espressif__tinyusb/src/class/msc/msc_host.h
Normal file
132
managed_components/espressif__tinyusb/src/class/msc/msc_host.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MSC_HOST_H_
|
||||
#define TUSB_MSC_HOST_H_
|
||||
|
||||
#include "msc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef CFG_TUH_MSC_MAXLUN
|
||||
#define CFG_TUH_MSC_MAXLUN 4
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
msc_cbw_t const* cbw; // SCSI command
|
||||
msc_csw_t const* csw; // SCSI status
|
||||
void* scsi_data; // SCSI Data
|
||||
uintptr_t user_arg; // user argument
|
||||
}tuh_msc_complete_data_t;
|
||||
|
||||
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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);
|
||||
|
||||
// Check if the interface is currently ready or busy transferring data
|
||||
bool tuh_msc_ready(uint8_t dev_addr);
|
||||
|
||||
// Get Max Lun
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
|
||||
|
||||
// Get number of block
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
|
||||
|
||||
// Get block size in bytes
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
|
||||
|
||||
// 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.
|
||||
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Inquiry command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// 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, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Request Sense 10 command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Read Capacity 10 command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
|
||||
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
//------------- Application Callback -------------//
|
||||
|
||||
// Invoked when a device with MassStorage interface is mounted
|
||||
void tuh_msc_mount_cb(uint8_t dev_addr);
|
||||
|
||||
// Invoked when a device with MassStorage interface is unmounted
|
||||
void tuh_msc_umount_cb(uint8_t dev_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool msch_init (void);
|
||||
bool msch_deinit (void);
|
||||
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool msch_set_config (uint8_t daddr, uint8_t itf_num);
|
||||
void msch_close (uint8_t dev_addr);
|
||||
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
888
managed_components/espressif__tinyusb/src/class/mtp/mtp.h
Normal file
888
managed_components/espressif__tinyusb/src/class/mtp/mtp.h
Normal file
@@ -0,0 +1,888 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com)
|
||||
*
|
||||
* 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_MTP_H_
|
||||
#define TUSB_MTP_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_MTP)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Media Transfer Protocol Class Constant
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Media Transfer Protocol Subclass
|
||||
typedef enum {
|
||||
MTP_SUBCLASS_STILL_IMAGE = 1
|
||||
} mtp_subclass_type_t;
|
||||
|
||||
// MTP Protocol.
|
||||
typedef enum {
|
||||
MTP_PROTOCOL_PIMA_15470 = 1, ///< Picture Transfer Protocol (PIMA 15470)
|
||||
} mtp_protocol_type_t;
|
||||
|
||||
// PTP/MTP protocol phases
|
||||
typedef enum {
|
||||
MTP_PHASE_COMMAND = 0,
|
||||
MTP_PHASE_DATA,
|
||||
MTP_PHASE_RESPONSE,
|
||||
MTP_PHASE_ERROR
|
||||
} mtp_phase_type_t;
|
||||
|
||||
// PTP/MTP Class requests, PIMA 15740-2000: D.5.2
|
||||
typedef enum {
|
||||
MTP_REQ_CANCEL = 0x64,
|
||||
MTP_REQ_GET_EXT_EVENT_DATA = 0x65,
|
||||
MTP_REQ_RESET = 0x66,
|
||||
MTP_REQ_GET_DEVICE_STATUS = 0x67,
|
||||
} mtp_class_request_t;
|
||||
|
||||
// PTP/MTP Container type
|
||||
typedef enum {
|
||||
MTP_CONTAINER_TYPE_UNDEFINED = 0,
|
||||
MTP_CONTAINER_TYPE_COMMAND_BLOCK = 1,
|
||||
MTP_CONTAINER_TYPE_DATA_BLOCK = 2,
|
||||
MTP_CONTAINER_TYPE_RESPONSE_BLOCK = 3,
|
||||
MTP_CONTAINER_TYPE_EVENT_BLOCK = 4,
|
||||
} mtp_container_type_t;
|
||||
|
||||
// MTP 1.1 Appendix A: Object formats
|
||||
typedef enum {
|
||||
// ---- Base formats ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED = 0x3000u, // Undefined object
|
||||
MTP_OBJ_FORMAT_ASSOCIATION = 0x3001u, // Association (for example, a folder)
|
||||
MTP_OBJ_FORMAT_SCRIPT = 0x3002u, // Device model-specific script
|
||||
MTP_OBJ_FORMAT_EXECUTABLE = 0x3003u, // Device model-specific binary executable
|
||||
MTP_OBJ_FORMAT_TEXT = 0x3004u, // Text file
|
||||
MTP_OBJ_FORMAT_HTML = 0x3005u, // Hypertext Markup Language file (text)
|
||||
MTP_OBJ_FORMAT_DPOF = 0x3006u, // Digital Print Order Format file (text)
|
||||
MTP_OBJ_FORMAT_AIFF = 0x3007u, // Audio clip (AIFF)
|
||||
MTP_OBJ_FORMAT_WAV = 0x3008u, // Audio clip (WAV)
|
||||
MTP_OBJ_FORMAT_MP3 = 0x3009u, // MPEG-1 Layer III audio (ISO/IEC 13818-3)
|
||||
MTP_OBJ_FORMAT_AVI = 0x300Au, // Video clip (AVI)
|
||||
MTP_OBJ_FORMAT_MPEG = 0x300Bu, // Video clip (MPEG)
|
||||
MTP_OBJ_FORMAT_ASF = 0x300Cu, // Microsoft Advanced Streaming Format (video)
|
||||
|
||||
// ---- Image formats ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_IMAGE = 0x3800u, // Undefined image object
|
||||
MTP_OBJ_FORMAT_EXIF_JPEG = 0x3801u, // Exchangeable Image Format, JEIDA standard
|
||||
MTP_OBJ_FORMAT_TIFF_EP = 0x3802u, // Tag Image File Format for Electronic Photography
|
||||
MTP_OBJ_FORMAT_FLASHPIX = 0x3803u, // Structured Storage Image Format (FlashPix)
|
||||
MTP_OBJ_FORMAT_BMP = 0x3804u, // Microsoft Windows Bitmap file
|
||||
MTP_OBJ_FORMAT_CIFF = 0x3805u, // Canon Camera Image File Format
|
||||
MTP_OBJ_FORMAT_UNDEFINED_3806 = 0x3806u, // Reserved / Undefined
|
||||
MTP_OBJ_FORMAT_GIF = 0x3807u, // Graphics Interchange Format
|
||||
MTP_OBJ_FORMAT_JFIF = 0x3808u, // JPEG File Interchange Format
|
||||
MTP_OBJ_FORMAT_CD = 0x3809u, // PhotoCD Image Pac
|
||||
MTP_OBJ_FORMAT_PICT = 0x380Au, // Quickdraw Image Format
|
||||
MTP_OBJ_FORMAT_PNG = 0x380Bu, // Portable Network Graphics
|
||||
MTP_OBJ_FORMAT_UNDEFINED_380C = 0x380Cu, // Reserved / Undefined
|
||||
MTP_OBJ_FORMAT_TIFF = 0x380Du, // Tag Image File Format (baseline)
|
||||
MTP_OBJ_FORMAT_TIFF_IT = 0x380Eu, // Tag Image File Format for IT (graphic arts)
|
||||
MTP_OBJ_FORMAT_JP2 = 0x380Fu, // JPEG2000 Baseline File Format
|
||||
MTP_OBJ_FORMAT_JPX = 0x3810u, // JPEG2000 Extended File Format
|
||||
|
||||
// ---- Firmware & misc ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_FIRMWARE = 0xB802u, // Undefined Firmware
|
||||
MTP_OBJ_FORMAT_WBMP = 0xB803u, // Wireless Application Protocol Bitmap Format (.wbmp)
|
||||
MTP_OBJ_FORMAT_WINDOWS_IMAGE = 0xB881u, // Windows Image Format
|
||||
MTP_OBJ_FORMAT_JPEGXR = 0xB804u, // JPEG XR (.hdp, .jxr, .wdp)
|
||||
|
||||
// ---- Audio formats ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_AUDIO = 0xB900u, // Undefined audio object
|
||||
MTP_OBJ_FORMAT_WMA = 0xB901u, // Windows Media Audio
|
||||
MTP_OBJ_FORMAT_OGG = 0xB902u, // OGG container
|
||||
MTP_OBJ_FORMAT_AAC = 0xB903u, // Advanced Audio Coding (.aac)
|
||||
MTP_OBJ_FORMAT_AUDIBLE = 0xB904u, // Audible format
|
||||
MTP_OBJ_FORMAT_FLAC = 0xB906u, // Free Lossless Audio Codec
|
||||
MTP_OBJ_FORMAT_QCELP = 0xB907u, // Qualcomm Code Excited Linear Prediction (.qcp)
|
||||
MTP_OBJ_FORMAT_AMR = 0xB908u, // Adaptive Multi-Rate audio (.amr)
|
||||
|
||||
// ---- Video formats ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_VIDEO = 0xB980u, // Undefined video object
|
||||
MTP_OBJ_FORMAT_WMV = 0xB981u, // Windows Media Video
|
||||
MTP_OBJ_FORMAT_MP4 = 0xB982u, // MP4 Container (ISO 14496-1)
|
||||
MTP_OBJ_FORMAT_MP2 = 0xB983u, // MPEG-1 Layer II audio
|
||||
MTP_OBJ_FORMAT_3GP = 0xB984u, // 3GP Container
|
||||
MTP_OBJ_FORMAT_3G2 = 0xB985u, // 3GPP2 Container
|
||||
MTP_OBJ_FORMAT_AVCHD = 0xB986u, // AVCHD (MPEG-4 AVC + Dolby Digital)
|
||||
MTP_OBJ_FORMAT_ATSC_TS = 0xB987u, // ATSC-compliant MPEG-2 Transport Stream
|
||||
MTP_OBJ_FORMAT_DVB_TS = 0xB988u, // DVB-compliant MPEG-2 Transport Stream
|
||||
|
||||
// ---- Collections ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_COLLECTION = 0xBA00u, // Undefined collection
|
||||
MTP_OBJ_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM = 0xBA01u, // Abstract Multimedia Album
|
||||
MTP_OBJ_FORMAT_ABSTRACT_IMAGE_ALBUM = 0xBA02u, // Abstract Image Album
|
||||
MTP_OBJ_FORMAT_ABSTRACT_AUDIO_ALBUM = 0xBA03u, // Abstract Audio Album
|
||||
MTP_OBJ_FORMAT_ABSTRACT_VIDEO_ALBUM = 0xBA04u, // Abstract Video Album
|
||||
MTP_OBJ_FORMAT_ABSTRACT_AV_PLAYLIST = 0xBA05u, // Abstract Audio & Video Playlist
|
||||
MTP_OBJ_FORMAT_ABSTRACT_CONTACT_GROUP = 0xBA06u, // Abstract Contact Group
|
||||
MTP_OBJ_FORMAT_ABSTRACT_MESSAGE_FOLDER = 0xBA07u, // Abstract Message Folder
|
||||
MTP_OBJ_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION = 0xBA08u, // Abstract Chaptered Production
|
||||
MTP_OBJ_FORMAT_ABSTRACT_AUDIO_PLAYLIST = 0xBA09u, // Abstract Audio Playlist
|
||||
MTP_OBJ_FORMAT_ABSTRACT_VIDEO_PLAYLIST = 0xBA0Au, // Abstract Video Playlist
|
||||
MTP_OBJ_FORMAT_ABSTRACT_MEDIACAST = 0xBA0Bu, // Abstract Mediacast (RSS enclosure)
|
||||
|
||||
// ---- Playlist formats ----
|
||||
MTP_OBJ_FORMAT_WPL_PLAYLIST = 0xBA10u, // Windows Media Player Playlist (.wpl)
|
||||
MTP_OBJ_FORMAT_M3U_PLAYLIST = 0xBA11u, // M3U Playlist
|
||||
MTP_OBJ_FORMAT_MPL_PLAYLIST = 0xBA12u, // MPL Playlist
|
||||
MTP_OBJ_FORMAT_ASX_PLAYLIST = 0xBA13u, // ASX Playlist
|
||||
MTP_OBJ_FORMAT_PLS_PLAYLIST = 0xBA14u, // PLS Playlist
|
||||
|
||||
// ---- Document formats ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_DOC = 0xBA80u, // Undefined Document
|
||||
MTP_OBJ_FORMAT_ABSTRACT_DOC = 0xBA81u, // Abstract Document
|
||||
MTP_OBJ_FORMAT_XML_DOC = 0xBA82u, // XML Document
|
||||
MTP_OBJ_FORMAT_DOC = 0xBA83u, // Microsoft Word Document
|
||||
MTP_OBJ_FORMAT_MHT_DOC = 0xBA84u, // MHT Compiled HTML Document
|
||||
MTP_OBJ_FORMAT_XLS = 0xBA85u, // Microsoft Excel Spreadsheet
|
||||
MTP_OBJ_FORMAT_PPT = 0xBA86u, // Microsoft PowerPoint Presentation
|
||||
|
||||
// ---- Messaging ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_MSG = 0xBB00u, // Undefined Message
|
||||
MTP_OBJ_FORMAT_ABSTRACT_MSG = 0xBB01u, // Abstract Message
|
||||
|
||||
// ---- Bookmarks ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_BOOKMARK = 0xBB10u, // Undefined Bookmark
|
||||
MTP_OBJ_FORMAT_ABSTRACT_BOOKMARK = 0xBB11u, // Abstract Bookmark
|
||||
|
||||
// ---- Appointments ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_APPT = 0xBB20u, // Undefined Appointment
|
||||
MTP_OBJ_FORMAT_ABSTRACT_APPT = 0xBB21u, // Abstract Appointment
|
||||
MTP_OBJ_FORMAT_VCALENDAR1 = 0xBB22u, // vCalendar 1.0
|
||||
|
||||
// ---- Tasks ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_TASK = 0xBB40u, // Undefined Task
|
||||
MTP_OBJ_FORMAT_ABSTRACT_TASK = 0xBB41u, // Abstract Task
|
||||
MTP_OBJ_FORMAT_ICALENDAR = 0xBB42u, // iCalendar
|
||||
|
||||
// ---- Notes ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_NOTE = 0xBB60u, // Undefined Note
|
||||
MTP_OBJ_FORMAT_ABSTRACT_NOTE = 0xBB61u, // Abstract Note
|
||||
|
||||
// ---- Contacts ----
|
||||
MTP_OBJ_FORMAT_UNDEFINED_CONTACT= 0xBB80u, // Undefined Contact
|
||||
MTP_OBJ_FORMAT_ABSTRACT_CONTACT = 0xBB81u, // Abstract Contact
|
||||
MTP_OBJ_FORMAT_VCARD2 = 0xBB82u, // vCard 2.1
|
||||
MTP_OBJ_FORMAT_VCARD3 = 0xBB83u, // vCard 3.0
|
||||
} mtp_object_formats_t;
|
||||
|
||||
// MTP 1.1 Appendix B: Object Properties
|
||||
typedef enum {
|
||||
MTP_OBJ_PROP_STORAGE_ID = 0xDC01u, // StorageID
|
||||
MTP_OBJ_PROP_OBJECT_FORMAT = 0xDC02u, // Object Format
|
||||
MTP_OBJ_PROP_PROTECTION_STATUS = 0xDC03u, // Protection Status
|
||||
MTP_OBJ_PROP_OBJECT_SIZE = 0xDC04u, // Object Size
|
||||
MTP_OBJ_PROP_ASSOCIATION_TYPE = 0xDC05u, // Association Type
|
||||
MTP_OBJ_PROP_ASSOCIATION_DESC = 0xDC06u, // Association Description
|
||||
MTP_OBJ_PROP_OBJECT_FILE_NAME = 0xDC07u, // Object File Name
|
||||
MTP_OBJ_PROP_DATE_CREATED = 0xDC08u, // Date Created
|
||||
MTP_OBJ_PROP_DATE_MODIFIED = 0xDC09u, // Date Modified
|
||||
MTP_OBJ_PROP_KEYWORDS = 0xDC0Au, // Keywords
|
||||
MTP_OBJ_PROP_PARENT_OBJECT = 0xDC0Bu, // Parent Object
|
||||
MTP_OBJ_PROP_ALLOWED_FOLDER_CONTENTS = 0xDC0Cu, // Allowed Folder Contents
|
||||
MTP_OBJ_PROP_HIDDEN = 0xDC0Du, // Hidden
|
||||
MTP_OBJ_PROP_SYSTEM_OBJECT = 0xDC0Eu, // System Object
|
||||
// 0xDC0F-0xDC40 is reserved
|
||||
|
||||
MTP_OBJ_PROP_PERSISTENT_UID = 0xDC41u, // Persistent Unique Object Identifier
|
||||
MTP_OBJ_PROP_SYNC_ID = 0xDC42u, // SyncID
|
||||
MTP_OBJ_PROP_PROPERTY_BAG = 0xDC43u, // Property Bag
|
||||
MTP_OBJ_PROP_NAME = 0xDC44u, // Name
|
||||
MTP_OBJ_PROP_CREATED_BY = 0xDC45u, // Created By
|
||||
MTP_OBJ_PROP_ARTIST = 0xDC46u, // Artist
|
||||
MTP_OBJ_PROP_DATE_AUTHORED = 0xDC47u, // Date Authored
|
||||
MTP_OBJ_PROP_DESCRIPTION = 0xDC48u, // Description
|
||||
MTP_OBJ_PROP_URL_REFERENCE = 0xDC49u, // URL Reference
|
||||
MTP_OBJ_PROP_LANGUAGE_LOCALE = 0xDC4Au, // Language-Locale
|
||||
MTP_OBJ_PROP_COPYRIGHT_INFO = 0xDC4Bu, // Copyright Information
|
||||
MTP_OBJ_PROP_SOURCE = 0xDC4Cu, // Source
|
||||
MTP_OBJ_PROP_ORIGIN_LOCATION = 0xDC4Du, // Origin Location
|
||||
MTP_OBJ_PROP_DATE_ADDED = 0xDC4Eu, // Date Added
|
||||
MTP_OBJ_PROP_NON_CONSUMABLE = 0xDC4Fu, // Non-Consumable
|
||||
MTP_OBJ_PROP_CORRUPT_UNPLAYABLE = 0xDC50u, // Corrupt/Unplayable
|
||||
MTP_OBJ_PROP_PRODUCER_SERIAL_NUMBER = 0xDC51u, // ProducerSerialNumber
|
||||
// 0xDC52-0xDC80 is reserved
|
||||
|
||||
MTP_OBJ_PROP_REP_SAMPLE_FORMAT = 0xDC81u, // Representative Sample Format
|
||||
MTP_OBJ_PROP_REP_SAMPLE_SIZE = 0xDC82u, // Representative Sample Size
|
||||
MTP_OBJ_PROP_REP_SAMPLE_HEIGHT = 0xDC83u, // Representative Sample Height
|
||||
MTP_OBJ_PROP_REP_SAMPLE_WIDTH = 0xDC84u, // Representative Sample Width
|
||||
MTP_OBJ_PROP_REP_SAMPLE_DURATION = 0xDC85u, // Representative Sample Duration
|
||||
MTP_OBJ_PROP_REP_SAMPLE_DATA = 0xDC86u, // Representative Sample Data
|
||||
MTP_OBJ_PROP_WIDTH = 0xDC87u, // Width
|
||||
MTP_OBJ_PROP_HEIGHT = 0xDC88u, // Height
|
||||
MTP_OBJ_PROP_DURATION = 0xDC89u, // Duration
|
||||
MTP_OBJ_PROP_RATING = 0xDC8Au, // Rating
|
||||
MTP_OBJ_PROP_TRACK = 0xDC8Bu, // Track
|
||||
MTP_OBJ_PROP_GENRE = 0xDC8Cu, // Genre
|
||||
MTP_OBJ_PROP_CREDITS = 0xDC8Du, // Credits
|
||||
MTP_OBJ_PROP_LYRICS = 0xDC8Eu, // Lyrics
|
||||
MTP_OBJ_PROP_SUBSCRIPTION_CONTENT_ID = 0xDC8Fu, // Subscription Content ID
|
||||
MTP_OBJ_PROP_PRODUCED_BY = 0xDC90u, // Produced By
|
||||
MTP_OBJ_PROP_USE_COUNT = 0xDC91u, // Use Count
|
||||
MTP_OBJ_PROP_SKIP_COUNT = 0xDC92u, // Skip Count
|
||||
MTP_OBJ_PROP_LAST_ACCESSED = 0xDC93u, // Last Accessed
|
||||
MTP_OBJ_PROP_PARENTAL_RATING = 0xDC94u, // Parental Rating
|
||||
MTP_OBJ_PROP_META_GENRE = 0xDC95u, // Meta Genre
|
||||
MTP_OBJ_PROP_COMPOSER = 0xDC96u, // Composer
|
||||
MTP_OBJ_PROP_EFFECTIVE_RATING = 0xDC97u, // Effective Rating
|
||||
MTP_OBJ_PROP_SUBTITLE = 0xDC98u, // Subtitle
|
||||
MTP_OBJ_PROP_ORIGINAL_RELEASE_DATE = 0xDC99u, // Original Release Date
|
||||
MTP_OBJ_PROP_ALBUM_NAME = 0xDC9Au, // Album Name
|
||||
MTP_OBJ_PROP_ALBUM_ARTIST = 0xDC9Bu, // Album Artist
|
||||
MTP_OBJ_PROP_MOOD = 0xDC9Cu, // Mood
|
||||
MTP_OBJ_PROP_DRM_STATUS = 0xDC9Du, // DRM Status
|
||||
MTP_OBJ_PROP_SUB_DESCRIPTION = 0xDC9Eu, // Sub Description
|
||||
// 0xDC9F-0xDCD0 is reserved
|
||||
|
||||
MTP_OBJ_PROP_IS_CROPPED = 0xDCD1u, // Is Cropped
|
||||
MTP_OBJ_PROP_IS_COLOUR_CORRECTED = 0xDCD2u, // Is Colour Corrected
|
||||
MTP_OBJ_PROP_IMAGE_BIT_DEPTH = 0xDCD3u, // Image Bit Depth
|
||||
MTP_OBJ_PROP_FNUMBER = 0xDCD4u, // Fnumber (aperture ×100)
|
||||
MTP_OBJ_PROP_EXPOSURE_TIME = 0xDCD5u, // Exposure Time (sec ×10,000)
|
||||
MTP_OBJ_PROP_EXPOSURE_INDEX = 0xDCD6u, // Exposure Index (ISO)
|
||||
// 0xDCD7-0xDCDF is reserved
|
||||
|
||||
MTP_OBJ_PROP_DISPLAY_NAME = 0xDCE0u, // Display Name
|
||||
MTP_OBJ_PROP_BODY_TEXT = 0xDCE1u, // Body Text
|
||||
MTP_OBJ_PROP_SUBJECT = 0xDCE2u, // Subject
|
||||
MTP_OBJ_PROP_PRIORITY = 0xDCE3u, // Priority
|
||||
// 0xDCE4-0xDCFF is reserved
|
||||
|
||||
MTP_OBJ_PROP_GIVEN_NAME = 0xDD00u, // Given Name
|
||||
MTP_OBJ_PROP_MIDDLE_NAMES = 0xDD01u, // Middle Names
|
||||
MTP_OBJ_PROP_FAMILY_NAME = 0xDD02u, // Family Name
|
||||
MTP_OBJ_PROP_PREFIX = 0xDD03u, // Prefix
|
||||
MTP_OBJ_PROP_SUFFIX = 0xDD04u, // Suffix
|
||||
MTP_OBJ_PROP_PHONETIC_GIVEN_NAME = 0xDD05u, // Phonetic Given Name
|
||||
MTP_OBJ_PROP_PHONETIC_FAMILY_NAME = 0xDD06u, // Phonetic Family Name
|
||||
MTP_OBJ_PROP_EMAIL_PRIMARY = 0xDD07u, // Email Primary
|
||||
MTP_OBJ_PROP_EMAIL_PERSONAL_1 = 0xDD08u, // Email Personal 1
|
||||
MTP_OBJ_PROP_EMAIL_PERSONAL_2 = 0xDD09u, // Email Personal 2
|
||||
MTP_OBJ_PROP_EMAIL_BUSINESS_1 = 0xDD0Au, // Email Business 1
|
||||
MTP_OBJ_PROP_EMAIL_BUSINESS_2 = 0xDD0Bu, // Email Business 2
|
||||
MTP_OBJ_PROP_EMAIL_OTHERS = 0xDD0Cu, // Email Others
|
||||
MTP_OBJ_PROP_PHONE_PRIMARY = 0xDD0Du, // Phone Number Primary
|
||||
MTP_OBJ_PROP_PHONE_PERSONAL_1 = 0xDD0Eu, // Phone Number Personal
|
||||
MTP_OBJ_PROP_PHONE_PERSONAL_2 = 0xDD0Fu, // Phone Number Personal 2
|
||||
MTP_OBJ_PROP_PHONE_BUSINESS_1 = 0xDD10u, // Phone Number Business
|
||||
MTP_OBJ_PROP_PHONE_BUSINESS_2 = 0xDD11u, // Phone Number Business 2
|
||||
MTP_OBJ_PROP_PHONE_MOBILE_1 = 0xDD12u, // Phone Number Mobile
|
||||
MTP_OBJ_PROP_PHONE_MOBILE_2 = 0xDD13u, // Phone Number Mobile 2
|
||||
MTP_OBJ_PROP_FAX_PRIMARY = 0xDD14u, // Fax Number Primary
|
||||
MTP_OBJ_PROP_FAX_PERSONAL = 0xDD15u, // Fax Number Personal
|
||||
MTP_OBJ_PROP_FAX_BUSINESS = 0xDD16u, // Fax Number Business
|
||||
MTP_OBJ_PROP_PAGER_NUMBER = 0xDD17u, // Pager Number
|
||||
MTP_OBJ_PROP_PHONE_OTHERS = 0xDD18u, // Phone Number Others
|
||||
MTP_OBJ_PROP_WEB_PRIMARY = 0xDD19u, // Primary Web Address
|
||||
MTP_OBJ_PROP_WEB_PERSONAL = 0xDD1Au, // Personal Web Address
|
||||
MTP_OBJ_PROP_WEB_BUSINESS = 0xDD1Bu, // Business Web Address
|
||||
MTP_OBJ_PROP_IM_ADDRESS_1 = 0xDD1Cu, // Instant Messenger Address
|
||||
MTP_OBJ_PROP_IM_ADDRESS_2 = 0xDD1Du, // Instant Messenger Address 2
|
||||
MTP_OBJ_PROP_IM_ADDRESS_3 = 0xDD1Eu, // Instant Messenger Address 3
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_FULL = 0xDD1Fu, // Postal Address Personal Full
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_LINE1 = 0xDD20u, // Postal Address Personal Line 1
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_LINE2 = 0xDD21u, // Postal Address Personal Line 2
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_CITY = 0xDD22u, // Postal Address Personal City
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_REGION = 0xDD23u, // Postal Address Personal Region
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_POSTAL_CODE = 0xDD24u, // Postal Address Personal Postal Code
|
||||
MTP_OBJ_PROP_ADDR_PERSONAL_COUNTRY = 0xDD25u, // Postal Address Personal Country
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_FULL = 0xDD26u, // Postal Address Business Full
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_LINE1 = 0xDD27u, // Postal Address Business Line 1
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_LINE2 = 0xDD28u, // Postal Address Business Line 2
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_CITY = 0xDD29u, // Postal Address Business City
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_REGION = 0xDD2Au, // Postal Address Business Region
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_POSTAL_CODE = 0xDD2Bu, // Postal Address Business Postal Code
|
||||
MTP_OBJ_PROP_ADDR_BUSINESS_COUNTRY = 0xDD2Cu, // Postal Address Business Country
|
||||
MTP_OBJ_PROP_ADDR_OTHER_FULL = 0xDD2Du, // Postal Address Other Full
|
||||
MTP_OBJ_PROP_ADDR_OTHER_LINE1 = 0xDD2Eu, // Postal Address Other Line 1
|
||||
MTP_OBJ_PROP_ADDR_OTHER_LINE2 = 0xDD2Fu, // Postal Address Other Line 2
|
||||
MTP_OBJ_PROP_ADDR_OTHER_CITY = 0xDD30u, // Postal Address Other City
|
||||
MTP_OBJ_PROP_ADDR_OTHER_REGION = 0xDD31u, // Postal Address Other Region
|
||||
MTP_OBJ_PROP_ADDR_OTHER_POSTAL_CODE = 0xDD32u, // Postal Address Other Postal Code
|
||||
MTP_OBJ_PROP_ADDR_OTHER_COUNTRY = 0xDD33u, // Postal Address Other Country
|
||||
MTP_OBJ_PROP_ORGANIZATION_NAME = 0xDD34u, // Organization Name
|
||||
MTP_OBJ_PROP_PHONETIC_ORG_NAME = 0xDD35u, // Phonetic Organization Name
|
||||
MTP_OBJ_PROP_ROLE = 0xDD36u, // Role
|
||||
MTP_OBJ_PROP_BIRTHDATE = 0xDD37u, // Birthdate
|
||||
// 0xDD38-0xDD3F is reserved
|
||||
|
||||
MTP_OBJ_PROP_MESSAGE_TO = 0xDD40u, // Message To
|
||||
MTP_OBJ_PROP_MESSAGE_CC = 0xDD41u, // Message CC
|
||||
MTP_OBJ_PROP_MESSAGE_BCC = 0xDD42u, // Message BCC
|
||||
MTP_OBJ_PROP_MESSAGE_READ = 0xDD43u, // Message Read
|
||||
MTP_OBJ_PROP_MESSAGE_RECEIVED_TIME = 0xDD44u, // Message Received Time
|
||||
MTP_OBJ_PROP_MESSAGE_SENDER = 0xDD45u, // Message Sender
|
||||
// 0xDD46-0xDD4F is reserved
|
||||
|
||||
MTP_OBJ_PROP_ACTIVITY_BEGIN_TIME = 0xDD50u, // Activity Begin Time
|
||||
MTP_OBJ_PROP_ACTIVITY_END_TIME = 0xDD51u, // Activity End Time
|
||||
MTP_OBJ_PROP_ACTIVITY_LOCATION = 0xDD52u, // Activity Location
|
||||
// 0xDD53 is reserved
|
||||
MTP_OBJ_PROP_ACTIVITY_REQUIRED_ATTENDEES= 0xDD54u, // Activity Required Attendees
|
||||
MTP_OBJ_PROP_ACTIVITY_OPTIONAL_ATTENDEES= 0xDD55u, // Activity Optional Attendees
|
||||
MTP_OBJ_PROP_ACTIVITY_RESOURCES = 0xDD56u, // Activity Resources
|
||||
MTP_OBJ_PROP_ACTIVITY_ACCEPTED = 0xDD57u, // Activity Accepted
|
||||
MTP_OBJ_PROP_ACTIVITY_TENTATIVE = 0xDD58u, // Activity Tentative
|
||||
MTP_OBJ_PROP_ACTIVITY_DECLINED = 0xDD59u, // Activity Declined
|
||||
MTP_OBJ_PROP_ACTIVITY_REMINDER_TIME = 0xDD5Au, // Activity Reminder Time
|
||||
MTP_OBJ_PROP_ACTIVITY_OWNER = 0xDD5Bu, // Activity Owner
|
||||
MTP_OBJ_PROP_ACTIVITY_STATUS = 0xDD5Cu, // Activity Status
|
||||
MTP_OBJ_PROP_OWNER = 0xDD5Du, // Owner
|
||||
MTP_OBJ_PROP_EDITOR = 0xDD5Eu, // Editor
|
||||
MTP_OBJ_PROP_WEBMASTER = 0xDD5Fu, // Webmaster
|
||||
|
||||
MTP_OBJ_PROP_URL_SOURCE = 0xDD60u, // URL Source
|
||||
MTP_OBJ_PROP_URL_DESTINATION = 0xDD61u, // URL Destination
|
||||
MTP_OBJ_PROP_TIME_BOOKMARK = 0xDD62u, // Time Bookmark
|
||||
MTP_OBJ_PROP_OBJECT_BOOKMARK = 0xDD63u, // Object Bookmark
|
||||
MTP_OBJ_PROP_BYTE_BOOKMARK = 0xDD64u, // Byte Bookmark
|
||||
// 0xDD65-0xDD6F is reserved
|
||||
|
||||
MTP_OBJ_PROP_LAST_BUILD_DATE = 0xDD70u, // Last Build Date
|
||||
MTP_OBJ_PROP_TIME_TO_LIVE = 0xDD71u, // Time to Live (minutes)
|
||||
MTP_OBJ_PROP_MEDIA_GUID = 0xDD72u, // Media GUID
|
||||
// 0xDD73-0xDDFF is reserved
|
||||
|
||||
// media encoding
|
||||
MTP_OBJ_PROP_TOTAL_BITRATE = 0xDE91u, // Total BitRate
|
||||
MTP_OBJ_PROP_BITRATE_TYPE = 0xDE92u, // Bitrate Type
|
||||
MTP_OBJ_PROP_SAMPLE_RATE = 0xDE93u, // Sample Rate
|
||||
MTP_OBJ_PROP_NUM_CHANNELS = 0xDE94u, // Number Of Channels
|
||||
MTP_OBJ_PROP_AUDIO_BITDEPTH = 0xDE95u, // Audio BitDepth
|
||||
// 0xDE96 is reserved
|
||||
MTP_OBJ_PROP_SCAN_TYPE = 0xDE97u, // Scan Type
|
||||
// 0xDE98 is reserved
|
||||
MTP_OBJ_PROP_AUDIO_WAVE_CODEC = 0xDE99u, // Audio WAVE Codec
|
||||
MTP_OBJ_PROP_AUDIO_BITRATE = 0xDE9Au, // Audio BitRate
|
||||
MTP_OBJ_PROP_VIDEO_FOURCC_CODEC = 0xDE9Bu, // Video FourCC Codec
|
||||
MTP_OBJ_PROP_VIDEO_BITRATE = 0xDE9Cu, // Video BitRate
|
||||
MTP_OBJ_PROP_FRAMES_PER_KSEC = 0xDE9Du, // Frames Per Thousand Seconds
|
||||
MTP_OBJ_PROP_KEYFRAME_DISTANCE = 0xDE9Eu, // KeyFrame Distance (ms)
|
||||
MTP_OBJ_PROP_BUFFER_SIZE = 0xDE9Fu, // Buffer Size
|
||||
MTP_OBJ_PROP_ENCODING_QUALITY = 0xDEA0u, // Encoding Quality
|
||||
MTP_OBJ_PROP_ENCODING_PROFILE = 0xDEA1u // Encoding Profile
|
||||
} mtp_object_properties_t;
|
||||
|
||||
|
||||
// MTP 1.1 Appendeix C: Device Properties
|
||||
typedef enum {
|
||||
MTP_DEV_PROP_UNDEFINED = 0x5000u,
|
||||
MTP_DEV_PROP_BATTERY_LEVEL = 0x5001u,
|
||||
MTP_DEV_PROP_FUNCTIONAL_MODE = 0x5002u,
|
||||
MTP_DEV_PROP_IMAGE_SIZE = 0x5003u,
|
||||
MTP_DEV_PROP_COMPRESSION_SETTING = 0x5004u,
|
||||
MTP_DEV_PROP_WHITE_BALANCE = 0x5005u,
|
||||
MTP_DEV_PROP_RGB_GAIN = 0x5006u,
|
||||
MTP_DEV_PROP_F_NUMBER = 0x5007u,
|
||||
MTP_DEV_PROP_FOCAL_LENGTH = 0x5008u,
|
||||
MTP_DEV_PROP_FOCUS_DISTANCE = 0x5009u,
|
||||
MTP_DEV_PROP_FOCUS_MODE = 0x500Au,
|
||||
MTP_DEV_PROP_EXPOSURE_METERING_MODE = 0x500Bu,
|
||||
MTP_DEV_PROP_FLASH_MODE = 0x500Cu,
|
||||
MTP_DEV_PROP_EXPOSURE_TIME = 0x500Du,
|
||||
MTP_DEV_PROP_EXPOSURE_PROGRAM_MODE = 0x500Eu,
|
||||
MTP_DEV_PROP_EXPOSURE_INDEX = 0x500Fu,
|
||||
MTP_DEV_PROP_EXPOSURE_BIAS_COMPENSATION = 0x5010u,
|
||||
MTP_DEV_PROP_DATE_TIME = 0x5011u,
|
||||
MTP_DEV_PROP_CAPTURE_DELAY = 0x5012u,
|
||||
MTP_DEV_PROP_STILL_CAPTURE_MODE = 0x5013u,
|
||||
MTP_DEV_PROP_CONTRAST = 0x5014u,
|
||||
MTP_DEV_PROP_SHARPNESS = 0x5015u,
|
||||
MTP_DEV_PROP_DIGITAL_ZOOM = 0x5016u,
|
||||
MTP_DEV_PROP_EFFECT_MODE = 0x5017u,
|
||||
MTP_DEV_PROP_BURST_NUMBER = 0x5018u,
|
||||
MTP_DEV_PROP_BURST_INTERVAL = 0x5019u,
|
||||
MTP_DEV_PROP_TIMELAPSE_NUMBER = 0x501Au,
|
||||
MTP_DEV_PROP_TIMELAPSE_INTERVAL = 0x501Bu,
|
||||
MTP_DEV_PROP_FOCUS_METERING_MODE = 0x501Cu,
|
||||
MTP_DEV_PROP_UPLOAD_URL = 0x501Du,
|
||||
MTP_DEV_PROP_ARTIST = 0x501Eu,
|
||||
MTP_DEV_PROP_COPYRIGHT_INFO = 0x501Fu,
|
||||
MTP_DEV_PROP_SYNCHRONIZTION_PARTNER = 0xD401,
|
||||
MTP_DEV_PROP_DEVICE_FRIENDLY_NAME = 0xD402u,
|
||||
MTP_DEV_PROP_VOLUME = 0xD403u,
|
||||
MTP_DEV_PROP_SUPPORTED_FORMATS_ORDERED = 0xD404u,
|
||||
MTP_DEV_PROP_DEVICE_ICON = 0xD405u,
|
||||
MTP_DEV_PROP_SECTION_INITIATOR_VERSION_INFO = 0xD406u,
|
||||
MTP_DEV_PROP_PERCEIVED_DEVICE_TYPE = 0xD407u,
|
||||
MTP_DEV_PROP_PLAYBACK_RATE = 0xD410u,
|
||||
MTP_DEV_PROP_PLAYBACK_OBJECT = 0xD411u,
|
||||
MTP_DEV_PROP_PLAYBACK_CONTAINER_INDEX = 0xD412u,
|
||||
} mtp_event_properties_t;
|
||||
|
||||
// MTP 1.1 Appendix D: Operations
|
||||
typedef enum {
|
||||
MTP_OP_UNDEFINED = 0x1000u,
|
||||
MTP_OP_GET_DEVICE_INFO = 0x1001u,
|
||||
MTP_OP_OPEN_SESSION = 0x1002u,
|
||||
MTP_OP_CLOSE_SESSION = 0x1003u,
|
||||
MTP_OP_GET_STORAGE_IDS = 0x1004u,
|
||||
MTP_OP_GET_STORAGE_INFO = 0x1005u,
|
||||
MTP_OP_GET_NUM_OBJECTS = 0x1006u,
|
||||
MTP_OP_GET_OBJECT_HANDLES = 0x1007u,
|
||||
MTP_OP_GET_OBJECT_INFO = 0x1008u,
|
||||
MTP_OP_GET_OBJECT = 0x1009u,
|
||||
MTP_OP_GET_THUMB = 0x100Au,
|
||||
MTP_OP_DELETE_OBJECT = 0x100Bu,
|
||||
MTP_OP_SEND_OBJECT_INFO = 0x100Cu,
|
||||
MTP_OP_SEND_OBJECT = 0x100Du,
|
||||
MTP_OP_INITIATE_CAPTURE = 0x100Eu,
|
||||
MTP_OP_FORMAT_STORE = 0x100Fu,
|
||||
MTP_OP_RESET_DEVICE = 0x1010u,
|
||||
MTP_OP_SELF_TEST = 0x1011u,
|
||||
MTP_OP_SET_OBJECT_PROTECTION = 0x1012u,
|
||||
MTP_OP_POWER_DOWN = 0x1013u,
|
||||
MTP_OP_GET_DEVICE_PROP_DESC = 0x1014u,
|
||||
MTP_OP_GET_DEVICE_PROP_VALUE = 0x1015u,
|
||||
MTP_OP_SET_DEVICE_PROP_VALUE = 0x1016u,
|
||||
MTP_OP_RESET_DEVICE_PROP_VALUE = 0x1017u,
|
||||
MTP_OP_TERMINATE_OPEN_CAPTURE = 0x1018u,
|
||||
MTP_OP_MOVE_OBJECT = 0x1019u,
|
||||
MTP_OP_COPY_OBJECT = 0x101Au,
|
||||
MTP_OP_GET_PARTIAL_OBJECT = 0x101Bu,
|
||||
MTP_OP_INITIATE_OPEN_CAPTURE = 0x101Bu,
|
||||
MTP_OP_GET_OBJECT_PROPS_SUPPORTED = 0x9801u,
|
||||
MTP_OP_GET_OBJECT_PROP_DESC = 0x9802u,
|
||||
MTP_OP_GET_OBJECT_PROP_VALUE = 0x9803u,
|
||||
MTP_OP_SET_OBJECT_PROP_VALUE = 0x9804u,
|
||||
MTP_OP_GET_OBJECT_PROPLIST = 0x9805u,
|
||||
MTP_OP_GET_OBJECT_PROP_REFERENCES = 0x9810u,
|
||||
|
||||
MTP_OP_GET_SERVICE_IDS = 0x9301u,
|
||||
MTP_OP_GET_SERVICE_INFO = 0x9302u,
|
||||
MTP_OP_GET_SERVICE_CAPABILITIES = 0x9303u,
|
||||
MTP_OP_GET_SERVICE_PROP_DESC = 0x9304u,
|
||||
|
||||
// Appendix E: Enhanced Operations
|
||||
MTP_OP_GET_OBJECT_PROP_LIST = 0x9805u,
|
||||
MTP_OP_SET_OBJECT_PROP_LIST = 0x9806u,
|
||||
MTP_OP_GET_INTERDEPENDENT_PROP_DESC = 0x9807u,
|
||||
MTP_OP_SEND_OBJECT_PROP_LIST = 0x9808u,
|
||||
} mtp_operation_code_t;
|
||||
|
||||
// Appendix F: Responses
|
||||
typedef enum {
|
||||
MTP_RESP_UNDEFINED = 0x2000u,
|
||||
MTP_RESP_OK = 0x2001u,
|
||||
MTP_RESP_GENERAL_ERROR = 0x2002u,
|
||||
MTP_RESP_SESSION_NOT_OPEN = 0x2003u,
|
||||
MTP_RESP_INVALID_TRANSACTION_ID = 0x2004u,
|
||||
MTP_RESP_OPERATION_NOT_SUPPORTED = 0x2005u,
|
||||
MTP_RESP_PARAMETER_NOT_SUPPORTED = 0x2006u,
|
||||
MTP_RESP_INCOMPLETE_TRANSFER = 0x2007u,
|
||||
MTP_RESP_INVALID_STORAGE_ID = 0x2008u,
|
||||
MTP_RESP_INVALID_OBJECT_HANDLE = 0x2009u,
|
||||
MTP_RESP_DEVICE_PROP_NOT_SUPPORTED = 0x200Au,
|
||||
MTP_RESP_INVALID_OBJECT_FORMAT_CODE = 0x200Bu,
|
||||
MTP_RESP_STORE_FULL = 0x200Cu,
|
||||
MTP_RESP_OBJECT_WRITE_PROTECTED = 0x200Du,
|
||||
MPT_RESC_STORE_READ_ONLY = 0x200Eu,
|
||||
MTP_RESP_ACCESS_DENIED = 0x200Fu,
|
||||
MTP_RESP_NO_THUMBNAIL_PRESENT = 0x2010u,
|
||||
MTP_RESP_SELF_TEST_FAILED = 0x2011u,
|
||||
MTP_RESP_PARTIAL_DELETION = 0x2012u,
|
||||
MTP_RESP_STORE_NOT_AVAILABLE = 0x2013u,
|
||||
MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014u,
|
||||
MTP_RESP_NO_VALID_OBJECTINFO = 0x2015u,
|
||||
MTP_RESP_INVALID_CODE_FORMAT = 0x2016u,
|
||||
MTP_RESP_UNKNOWN_VENDOR_CODE = 0x2017u,
|
||||
MTP_RESP_CAPTURE_ALREADY_TERMINATED = 0x2018u,
|
||||
MTP_RESP_DEVICE_BUSY = 0x2019u,
|
||||
MTP_RESP_INVALID_PARENT_OBJECT = 0x201Au,
|
||||
MTP_RESP_INVALID_DEVICE_PROP_FORMAT = 0x201Bu,
|
||||
MTP_RESP_INVALID_DEVICE_PROP_VALUE = 0x201Cu,
|
||||
MTP_RESP_INVALID_PARAMETER = 0x201Du,
|
||||
MTP_RESP_SESSION_ALREADY_OPEN = 0x201Eu,
|
||||
MTP_RESP_TRANSACTION_CANCELLED = 0x201Fu,
|
||||
MTP_RESP_SPEC_OF_DESTINATION_UNSUPPORTED = 0x2020u,
|
||||
|
||||
MTP_RESP_INVALID_OBJECT_PROP_CODE = 0xA801u,
|
||||
MTP_RESP_INVALID_OBJECT_PROP_FORMAT = 0xA802u,
|
||||
MTP_RESP_INVALID_OBJECT_PROP_VALUE = 0xA803u,
|
||||
MTP_RESP_INVALID_OBJECT_REFERENCE = 0xA804u,
|
||||
MTP_RESP_GROUP_NOT_SUPPORTED = 0xA805u,
|
||||
MTP_RESP_INVALID_DATASET = 0xA806u,
|
||||
MTP_RESP_SPEC_BY_GROUP_UNSUPPORTED = 0xA807u,
|
||||
MTP_RESP_SPEC_BY_DEPTH_UNSUPPORTED = 0xA808u,
|
||||
MTP_RESP_OBJECT_TOO_LARGE = 0xA809u,
|
||||
MTP_RESP_OBJECT_PROP_NOT_SUPPORTED = 0xA80Au,
|
||||
} mtp_response_t;
|
||||
|
||||
// Appendix G: Events
|
||||
typedef enum {
|
||||
MTP_EVENT_UNDEFINED = 0x4000,
|
||||
MTP_EVENT_CANCEL_TRANSACTION = 0x4001,
|
||||
MTP_EVENT_OBJECT_ADDED = 0x4002,
|
||||
MTP_EVENT_OBJECT_REMOVED = 0x4003,
|
||||
MTP_EVENT_STORE_ADDED = 0x4004,
|
||||
MTP_EVENT_STORE_REMOVED = 0x4005,
|
||||
MTP_EVENT_DEVICE_PROP_CHANGED = 0x4006,
|
||||
MTP_EVENT_OBJECT_INFO_CHANGED = 0x4007,
|
||||
MTP_EVENT_DEVICE_INFO_CHANGED = 0x4008,
|
||||
MTP_EVENT_REQUEST_OBJECT_TRANSFER = 0x4009,
|
||||
MTP_EVENT_STORE_FULL = 0x400Au,
|
||||
MTP_EVENT_DEVICE_RESET = 0x400Bu,
|
||||
MTP_EVENT_STORAGE_INFO_CHANGED = 0x400Cu,
|
||||
MTP_EVENT_CAPTURE_COMPLETE = 0x400Du,
|
||||
MTP_EVENT_UNREPORTED_STATUS = 0x400Eu,
|
||||
MTP_EVENT_OBJECT_PROP_CHANGED = 0xC801u,
|
||||
MTP_EVENT_OBJECT_PROP_DESC_CHANGED = 0xC802u,
|
||||
MTP_EVENT_OBJECT_REFERENCES_CHANGED = 0xC803u,
|
||||
} mtp_event_code_t;
|
||||
|
||||
// Datatypes
|
||||
typedef enum {
|
||||
MTP_DATA_TYPE_UNDEFINED = 0x0000u,
|
||||
// scalars
|
||||
MTP_DATA_TYPE_INT8 = 0x0001u,
|
||||
MTP_DATA_TYPE_UINT8 = 0x0002u,
|
||||
MTP_DATA_TYPE_INT16 = 0x0003u,
|
||||
MTP_DATA_TYPE_UINT16 = 0x0004u,
|
||||
MTP_DATA_TYPE_INT32 = 0x0005u,
|
||||
MTP_DATA_TYPE_UINT32 = 0x0006u,
|
||||
MTP_DATA_TYPE_INT64 = 0x0007u,
|
||||
MTP_DATA_TYPE_UINT64 = 0x0008u,
|
||||
MTP_DATA_TYPE_INT128 = 0x0009u,
|
||||
MTP_DATA_TYPE_UINT128 = 0x000Au,
|
||||
// array
|
||||
MTP_DATA_TYPE_AINT8 = 0x4001u,
|
||||
MTP_DATA_TYPE_AUINT8 = 0x4002u,
|
||||
MTP_DATA_TYPE_AINT16 = 0x4003u,
|
||||
MTP_DATA_TYPE_AUINT16 = 0x4004u,
|
||||
MTP_DATA_TYPE_AINT32 = 0x4005u,
|
||||
MTP_DATA_TYPE_AUINT32 = 0x4006u,
|
||||
MTP_DATA_TYPE_AINT64 = 0x4007u,
|
||||
MTP_DATA_TYPE_AUINT64 = 0x4008u,
|
||||
MTP_DATA_TYPE_AINT128 = 0x4009u,
|
||||
MTP_DATA_TYPE_AUINT128 = 0x400Au,
|
||||
MTP_DATA_TYPE_STR = 0xFFFFu,
|
||||
} mtp_data_type_t;
|
||||
|
||||
// Get/Set
|
||||
typedef enum {
|
||||
MTP_MODE_GET = 0x00u,
|
||||
MTP_MODE_GET_SET = 0x01u,
|
||||
} mtp_mode_get_set_t;
|
||||
|
||||
typedef enum {
|
||||
MTP_STORAGE_TYPE_UNDEFINED = 0x0000u,
|
||||
MTP_STORAGE_TYPE_FIXED_ROM = 0x0001u,
|
||||
MTP_STORAGE_TYPE_REMOVABLE_ROM = 0x0002u,
|
||||
MTP_STORAGE_TYPE_FIXED_RAM = 0x0003u,
|
||||
MTP_STORAGE_TYPE_REMOVABLE_RAM = 0x0004u,
|
||||
} mtp_storage_type_t;
|
||||
|
||||
typedef enum {
|
||||
MTP_FILESYSTEM_TYPE_UNDEFINED = 0x0000u,
|
||||
MTP_FILESYSTEM_TYPE_GENERIC_FLAT = 0x0001u,
|
||||
MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL = 0x0002u,
|
||||
MTP_FILESYSTEM_TYPE_DCF = 0x0003u,
|
||||
} mtp_filesystem_type_t;
|
||||
|
||||
typedef enum {
|
||||
MTP_ACCESS_CAPABILITY_READ_WRITE = 0x0000u,
|
||||
MTP_ACCESS_CAPABILITY_READ_ONLY_WITHOUT_OBJECT_DELETION = 0x0001u,
|
||||
MTP_ACCESS_CAPABILITY_READ_ONLY_WITH_OBJECT_DELETION = 0x0002u,
|
||||
} mtp_access_capability_t;
|
||||
|
||||
typedef enum {
|
||||
MTP_PROTECTION_STATUS_NO_PROTECTION = 0x0000u,
|
||||
MTP_PROTECTION_STATUS_READ_ONLY = 0x0001u,
|
||||
MTP_PROTECTION_STATUS_READ_ONLY_DATA = 0x8002u,
|
||||
MTP_PROTECTION_NON_TRANSFERABLE_DATA = 0x8003u,
|
||||
} mtp_protection_status_t;
|
||||
|
||||
typedef enum {
|
||||
MTP_ASSOCIATION_UNDEFINED = 0x0000u,
|
||||
MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001u,
|
||||
MTP_ASSOCIATION_ALBUM = 0x0002u,
|
||||
MTP_ASSOCIATION_TIME_SEQUENCE = 0x0003u,
|
||||
MTP_ASSOCIATION_HORIZONTAL_PANORAMIC = 0x0004u,
|
||||
MTP_ASSOCIATION_VERTICAL_PANORAMIC = 0x0005u,
|
||||
MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u,
|
||||
MTP_ASSOCIATION_ANCILLARY_DATA = 0x0007u,
|
||||
} mtp_association_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Data structures
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t len;
|
||||
uint16_t type;
|
||||
uint16_t code;
|
||||
uint32_t transaction_id;
|
||||
} mtp_container_header_t;
|
||||
TU_VERIFY_STATIC(sizeof(mtp_container_header_t) == 12, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
mtp_container_header_t header;
|
||||
uint32_t params[5];
|
||||
} mtp_container_command_t;
|
||||
TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct");
|
||||
|
||||
// PTP/MTP Generic container
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
mtp_container_header_t header;
|
||||
uint8_t payload[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t))];
|
||||
} mtp_generic_container_t;
|
||||
|
||||
typedef struct {
|
||||
mtp_container_header_t* header;
|
||||
union {
|
||||
uint8_t* payload;
|
||||
uint16_t* payload16;
|
||||
uint32_t* payload32;
|
||||
};
|
||||
uint32_t payload_bytes; // available bytes for read/write
|
||||
} mtp_container_info_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint16_t code;
|
||||
uint32_t session_id;
|
||||
uint32_t transaction_id;
|
||||
uint32_t params[3];
|
||||
} mtp_event_t;
|
||||
TU_VERIFY_STATIC(sizeof(mtp_event_t) == 22, "size is not correct");
|
||||
|
||||
#define mtp_string_t(_nchars) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t count; /* in characters including null */ \
|
||||
uint16_t utf16[_nchars]; \
|
||||
}
|
||||
|
||||
#define mtp_array_t(_type, _count) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint32_t count; \
|
||||
_type arr[_count];\
|
||||
}
|
||||
|
||||
#define mtp_aint8_t(_count) mtp_array_t(int8_t, _count)
|
||||
#define mtp_auint16_t(_count) mtp_array_t(uint16_t, _count)
|
||||
#define mtp_auint32_t(_count) mtp_array_t(uint32_t, _count)
|
||||
#define mtp_auint64_t(_count) mtp_array_t(uint64_t, _count)
|
||||
|
||||
#define MTP_STORAGE_INFO_STRUCT(_storage_desc_chars, _volume_id_chars) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint16_t storage_type; \
|
||||
uint16_t filesystem_type; \
|
||||
uint16_t access_capability; \
|
||||
uint64_t max_capacity_in_bytes; \
|
||||
uint64_t free_space_in_bytes; \
|
||||
uint32_t free_space_in_objects; \
|
||||
mtp_string_t(_storage_desc_chars) storage_description; \
|
||||
mtp_string_t(_volume_id_chars) volume_identifier; \
|
||||
}
|
||||
|
||||
// Object Info Dataset without dynamic string: filename, date_created, date_modified, keywords
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t storage_id;
|
||||
uint16_t object_format;
|
||||
uint16_t protection_status;
|
||||
uint32_t object_compressed_size;
|
||||
uint16_t thumb_format;
|
||||
uint32_t thumb_compressed_size;
|
||||
uint32_t thumb_pix_width;
|
||||
uint32_t thumb_pix_height;
|
||||
uint32_t image_pix_width;
|
||||
uint32_t image_pix_height;
|
||||
uint32_t image_bit_depth;
|
||||
uint32_t parent_object; // 0: root
|
||||
uint16_t association_type;
|
||||
uint32_t association_desc;
|
||||
uint32_t sequence_number;
|
||||
// mtp_string_t() filename
|
||||
// mtp_string_t() date_created
|
||||
// mtp_string_t() date_modified
|
||||
// mtp_string_t() keywords
|
||||
} mtp_object_info_header_t;
|
||||
|
||||
// Device property desc up to get/set
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint16_t device_property_code;
|
||||
uint16_t datatype;
|
||||
uint8_t get_set;
|
||||
} mtp_device_prop_desc_header_t;
|
||||
|
||||
// The following fields will be dynamically added to the struct at runtime:
|
||||
// - wstring factory_def_value;
|
||||
// - wstring current_value_len;
|
||||
// - uint8_t form_flag;
|
||||
|
||||
// no form
|
||||
#define MTP_DEVICE_PROPERTIES_STRUCT(_type) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint16_t device_property_code; \
|
||||
uint16_t datatype; \
|
||||
uint8_t get_set; \
|
||||
_type factory_default; \
|
||||
_type current_value; \
|
||||
uint8_t form_flag; /* 0: none, 1: range, 2: enum */ \
|
||||
};
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint16_t code;
|
||||
uint32_t transaction_id;
|
||||
} mtp_request_reset_cancel_data_t;
|
||||
TU_VERIFY_STATIC(sizeof(mtp_request_reset_cancel_data_t) == 6, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Container helper function
|
||||
// return number of bytes added
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// return payload buffer for next write
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_ptr(mtp_container_info_t* p_container) {
|
||||
// only 1st packet include header
|
||||
uint32_t pos = p_container->header->len - sizeof(mtp_container_header_t);
|
||||
while (pos > CFG_TUD_MTP_EP_BUFSIZE) {
|
||||
pos -= CFG_TUD_MTP_EP_BUFSIZE;
|
||||
}
|
||||
return p_container->payload + pos;
|
||||
}
|
||||
|
||||
// only add_raw does partial copy
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) {
|
||||
uint8_t* buf = mtp_container_payload_ptr(p_container);
|
||||
const uint32_t added_len = tu_min32(len, CFG_TUD_MTP_EP_BUFSIZE - p_container->header->len);
|
||||
if (added_len > 0) {
|
||||
memcpy(buf, data, added_len);
|
||||
}
|
||||
p_container->header->len += len; // always increase len, even partial copy
|
||||
return added_len;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_container_info_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) {
|
||||
const uint32_t added_len = 4 + count * scalar_size;
|
||||
TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0);
|
||||
uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t);
|
||||
|
||||
tu_unaligned_write32(buf, count);
|
||||
p_container->header->len += 4;
|
||||
buf += 4;
|
||||
|
||||
memcpy(buf, data, count * scalar_size);
|
||||
p_container->header->len += count * scalar_size;
|
||||
|
||||
return added_len;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint16_t* utf16) {
|
||||
uint8_t count = 0;
|
||||
while (utf16[count]) {
|
||||
count++;
|
||||
}
|
||||
const uint32_t added_len = 1u + 2u * count;
|
||||
TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0);
|
||||
uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t);
|
||||
|
||||
*buf++ = count;
|
||||
p_container->header->len++;
|
||||
|
||||
memcpy(buf, utf16, 2 * count);
|
||||
p_container->header->len += 2 * count;
|
||||
|
||||
return added_len;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) {
|
||||
const uint8_t len = (uint8_t) (strlen(str) + 1); // include null
|
||||
TU_ASSERT(p_container->header->len + 1 + 2 * len < CFG_TUD_MTP_EP_BUFSIZE, 0);
|
||||
uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t);
|
||||
|
||||
if (len == 1) {
|
||||
// empty string (null only): single zero byte
|
||||
*buf = 0;
|
||||
p_container->header->len++;
|
||||
return 1;
|
||||
} else {
|
||||
*buf++ = len;
|
||||
p_container->header->len++;
|
||||
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
buf[0] = str[i];
|
||||
buf[1] = 0;
|
||||
buf += 2;
|
||||
p_container->header->len += 2;
|
||||
}
|
||||
return 1u + 2u * len;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_container_info_t* p_container, uint8_t data) {
|
||||
return mtp_container_add_raw(p_container, &data, 1);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint16(mtp_container_info_t* p_container, uint16_t data) {
|
||||
return mtp_container_add_raw(p_container, &data, 2);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint32(mtp_container_info_t* p_container, uint32_t data) {
|
||||
return mtp_container_add_raw(p_container, &data, 4);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint64(mtp_container_info_t* p_container, uint64_t data) {
|
||||
return mtp_container_add_raw(p_container, &data, 8);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint128(mtp_container_info_t* p_container, const void* data) {
|
||||
return mtp_container_add_raw(p_container, data, 16);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint8(mtp_container_info_t* p_container, uint32_t count, const uint8_t* data) {
|
||||
return mtp_container_add_array(p_container, sizeof(uint8_t), count, data);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint16(mtp_container_info_t* p_container, uint32_t count, const uint16_t* data) {
|
||||
return mtp_container_add_array(p_container, sizeof(uint16_t), count, data);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_container_info_t* p_container, uint32_t count, const uint32_t* data) {
|
||||
return mtp_container_add_array(p_container, sizeof(uint32_t), count, data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) {
|
||||
uint8_t nchars = *buf++;
|
||||
memcpy(utf16, buf, 2 * nchars);
|
||||
return 1u + 2u * nchars;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
540
managed_components/espressif__tinyusb/src/class/mtp/mtp_device.c
Normal file
540
managed_components/espressif__tinyusb/src/class/mtp/mtp_device.c
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_MTP)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "device/dcd.h"
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "mtp_device.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_MTP_LOG_LEVEL
|
||||
#define CFG_TUD_MTP_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MTP_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return false;
|
||||
}
|
||||
TU_ATTR_WEAK bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return false;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
TU_ATTR_WEAK bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return false;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
TU_ATTR_WEAK int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) {
|
||||
(void) cb_data;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// STRUCT
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t rhport;
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
uint8_t ep_event;
|
||||
|
||||
// Bulk Only Transfer (BOT) Protocol
|
||||
uint8_t phase;
|
||||
|
||||
uint32_t total_len;
|
||||
uint32_t xferred_len;
|
||||
|
||||
uint32_t session_id;
|
||||
mtp_container_command_t command;
|
||||
mtp_container_header_t io_header;
|
||||
|
||||
TU_ATTR_ALIGNED(4) uint8_t control_buf[CFG_TUD_MTP_EP_CONTROL_BUFSIZE];
|
||||
} mtpd_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(buf, CFG_TUD_MTP_EP_BUFSIZE);
|
||||
TUD_EPBUF_TYPE_DEF(mtp_event_t, buf_event);
|
||||
} mtpd_epbuf_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static mtpd_interface_t _mtpd_itf;
|
||||
CFG_TUD_MEM_SECTION static mtpd_epbuf_t _mtpd_epbuf;
|
||||
|
||||
static void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL
|
||||
|
||||
TU_ATTR_UNUSED static tu_lookup_entry_t const _mpt_op_lookup[] = {
|
||||
{.key = MTP_OP_UNDEFINED , .data = "Undefined" } ,
|
||||
{.key = MTP_OP_GET_DEVICE_INFO , .data = "GetDeviceInfo" } ,
|
||||
{.key = MTP_OP_OPEN_SESSION , .data = "OpenSession" } ,
|
||||
{.key = MTP_OP_CLOSE_SESSION , .data = "CloseSession" } ,
|
||||
{.key = MTP_OP_GET_STORAGE_IDS , .data = "GetStorageIDs" } ,
|
||||
{.key = MTP_OP_GET_STORAGE_INFO , .data = "GetStorageInfo" } ,
|
||||
{.key = MTP_OP_GET_NUM_OBJECTS , .data = "GetNumObjects" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_HANDLES , .data = "GetObjectHandles" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_INFO , .data = "GetObjectInfo" } ,
|
||||
{.key = MTP_OP_GET_OBJECT , .data = "GetObject" } ,
|
||||
{.key = MTP_OP_GET_THUMB , .data = "GetThumb" } ,
|
||||
{.key = MTP_OP_DELETE_OBJECT , .data = "DeleteObject" } ,
|
||||
{.key = MTP_OP_SEND_OBJECT_INFO , .data = "SendObjectInfo" } ,
|
||||
{.key = MTP_OP_SEND_OBJECT , .data = "SendObject" } ,
|
||||
{.key = MTP_OP_INITIATE_CAPTURE , .data = "InitiateCapture" } ,
|
||||
{.key = MTP_OP_FORMAT_STORE , .data = "FormatStore" } ,
|
||||
{.key = MTP_OP_RESET_DEVICE , .data = "ResetDevice" } ,
|
||||
{.key = MTP_OP_SELF_TEST , .data = "SelfTest" } ,
|
||||
{.key = MTP_OP_SET_OBJECT_PROTECTION , .data = "SetObjectProtection" } ,
|
||||
{.key = MTP_OP_POWER_DOWN , .data = "PowerDown" } ,
|
||||
{.key = MTP_OP_GET_DEVICE_PROP_DESC , .data = "GetDevicePropDesc" } ,
|
||||
{.key = MTP_OP_GET_DEVICE_PROP_VALUE , .data = "GetDevicePropValue" } ,
|
||||
{.key = MTP_OP_SET_DEVICE_PROP_VALUE , .data = "SetDevicePropValue" } ,
|
||||
{.key = MTP_OP_RESET_DEVICE_PROP_VALUE , .data = "ResetDevicePropValue" } ,
|
||||
{.key = MTP_OP_TERMINATE_OPEN_CAPTURE , .data = "TerminateOpenCapture" } ,
|
||||
{.key = MTP_OP_MOVE_OBJECT , .data = "MoveObject" } ,
|
||||
{.key = MTP_OP_COPY_OBJECT , .data = "CopyObject" } ,
|
||||
{.key = MTP_OP_GET_PARTIAL_OBJECT , .data = "GetPartialObject" } ,
|
||||
{.key = MTP_OP_INITIATE_OPEN_CAPTURE , .data = "InitiateOpenCapture" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROPS_SUPPORTED , .data = "GetObjectPropsSupported" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROP_DESC , .data = "GetObjectPropDesc" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROP_VALUE , .data = "GetObjectPropValue" } ,
|
||||
{.key = MTP_OP_SET_OBJECT_PROP_VALUE , .data = "SetObjectPropValue" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROPLIST , .data = "GetObjectPropList" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROP_REFERENCES , .data = "GetObjectPropReferences" } ,
|
||||
{.key = MTP_OP_GET_SERVICE_IDS , .data = "GetServiceIDs" } ,
|
||||
{.key = MTP_OP_GET_SERVICE_INFO , .data = "GetServiceInfo" } ,
|
||||
{.key = MTP_OP_GET_SERVICE_CAPABILITIES , .data = "GetServiceCapabilities" } ,
|
||||
{.key = MTP_OP_GET_SERVICE_PROP_DESC , .data = "GetServicePropDesc" } ,
|
||||
{.key = MTP_OP_GET_OBJECT_PROP_LIST , .data = "GetObjectPropList" } ,
|
||||
{.key = MTP_OP_SET_OBJECT_PROP_LIST , .data = "SetObjectPropList" } ,
|
||||
{.key = MTP_OP_GET_INTERDEPENDENT_PROP_DESC , .data = "GetInterdependentPropDesc" } ,
|
||||
{.key = MTP_OP_SEND_OBJECT_PROP_LIST , .data = "SendObjectPropList" }
|
||||
};
|
||||
|
||||
TU_ATTR_UNUSED static tu_lookup_table_t const _mtp_op_table = {
|
||||
.count = TU_ARRAY_SIZE(_mpt_op_lookup),
|
||||
.items = _mpt_op_lookup
|
||||
};
|
||||
|
||||
TU_ATTR_UNUSED static const char* _mtp_phase_str[] = {
|
||||
"Command",
|
||||
"Data",
|
||||
"Response",
|
||||
"Error"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
static bool prepare_new_command(mtpd_interface_t* p_mtp) {
|
||||
p_mtp->phase = MTP_PHASE_COMMAND;
|
||||
return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE);
|
||||
}
|
||||
|
||||
static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) {
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
if (p_mtp->phase == MTP_PHASE_COMMAND) {
|
||||
// 1st data block: header + payload
|
||||
p_mtp->phase = MTP_PHASE_DATA;
|
||||
p_mtp->xferred_len = 0;
|
||||
|
||||
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
|
||||
p_mtp->total_len = p_container->header->len;
|
||||
p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK;
|
||||
p_container->header->transaction_id = p_mtp->command.header.transaction_id;
|
||||
p_mtp->io_header = *p_container->header; // save header for subsequent data
|
||||
} else {
|
||||
// OUT transfer: total length is at least max packet size
|
||||
p_mtp->total_len = tu_max32(p_container->header->len, CFG_TUD_MTP_EP_BUFSIZE);
|
||||
}
|
||||
} else {
|
||||
// subsequent data block: payload only
|
||||
TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA);
|
||||
}
|
||||
|
||||
const uint16_t xact_len = (uint16_t) tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE);
|
||||
if (xact_len) {
|
||||
// already transferred all bytes in header's length. Application make an unnecessary extra call
|
||||
TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, ep_addr));
|
||||
TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_mtp_data_send(mtp_container_info_t* p_container) {
|
||||
return mtpd_data_xfer(p_container, _mtpd_itf.ep_in);
|
||||
}
|
||||
|
||||
bool tud_mtp_data_receive(mtp_container_info_t* p_container) {
|
||||
return mtpd_data_xfer(p_container, _mtpd_itf.ep_out);
|
||||
}
|
||||
|
||||
bool tud_mtp_response_send(mtp_container_info_t* p_container) {
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
p_mtp->phase = MTP_PHASE_RESPONSE;
|
||||
p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK;
|
||||
p_container->header->transaction_id = p_mtp->command.header.transaction_id;
|
||||
TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_in));
|
||||
return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len);
|
||||
}
|
||||
|
||||
bool tud_mtp_mounted(void) {
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
return p_mtp->ep_out != 0 && p_mtp->ep_in != 0;
|
||||
}
|
||||
|
||||
bool tud_mtp_event_send(mtp_event_t* event) {
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
TU_VERIFY(p_mtp->ep_event != 0);
|
||||
_mtpd_epbuf.buf_event = *event;
|
||||
TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_event)); // Claim the endpoint
|
||||
return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mtpd_init(void) {
|
||||
tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t));
|
||||
}
|
||||
|
||||
bool mtpd_deinit(void) {
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
void mtpd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t));
|
||||
}
|
||||
|
||||
uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
|
||||
// only support PIMA 15470 protocol
|
||||
TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass &&
|
||||
MTP_SUBCLASS_STILL_IMAGE == itf_desc->bInterfaceSubClass &&
|
||||
MTP_PROTOCOL_PIMA_15470 == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
// mtp driver length is fixed
|
||||
const uint16_t mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// Max length must be at least 1 interface + 3 endpoints
|
||||
TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= mtpd_itf_size);
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
tu_memclr(p_mtp, sizeof(mtpd_interface_t));
|
||||
p_mtp->rhport = rhport;
|
||||
p_mtp->itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
// Open interrupt IN endpoint
|
||||
const tusb_desc_endpoint_t* ep_desc = (const tusb_desc_endpoint_t*) tu_desc_next(itf_desc);
|
||||
TU_ASSERT(ep_desc->bDescriptorType == TUSB_DESC_ENDPOINT && ep_desc->bmAttributes.xfer == TUSB_XFER_INTERRUPT, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, ep_desc), 0);
|
||||
p_mtp->ep_event = ep_desc->bEndpointAddress;
|
||||
|
||||
// Open endpoint pair
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0);
|
||||
TU_ASSERT(prepare_new_command(p_mtp), 0);
|
||||
|
||||
return mtpd_itf_size;
|
||||
}
|
||||
|
||||
// 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 mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
tud_mtp_request_cb_data_t cb_data = {
|
||||
.idx = 0,
|
||||
.stage = stage,
|
||||
.session_id = p_mtp->session_id,
|
||||
.request = request,
|
||||
.buf = p_mtp->control_buf,
|
||||
.bufsize = tu_le16toh(request->wLength),
|
||||
};
|
||||
|
||||
switch (request->bRequest) {
|
||||
case MTP_REQ_CANCEL:
|
||||
TU_LOG_DRV(" MTP request: Cancel\n");
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
return tud_control_xfer(rhport, request, p_mtp->control_buf, CFG_TUD_MTP_EP_CONTROL_BUFSIZE);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
return tud_mtp_request_cancel_cb(&cb_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case MTP_REQ_GET_EXT_EVENT_DATA:
|
||||
TU_LOG_DRV(" MTP request: Get Extended Event Data\n");
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
const int32_t len = tud_mtp_request_get_extended_event_cb(&cb_data);
|
||||
TU_VERIFY(len > 0);
|
||||
return tud_control_xfer(rhport,request, p_mtp->control_buf, (uint16_t) len);
|
||||
}
|
||||
break;
|
||||
|
||||
case MTP_REQ_RESET:
|
||||
TU_LOG_DRV(" MTP request: Device Reset\n");
|
||||
// used by the host to return the Still Image Capture Device to the Idle state after the Bulk-pipe has stalled
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
// clear stalled
|
||||
if (usbd_edpt_stalled(rhport, p_mtp->ep_out)) {
|
||||
usbd_edpt_clear_stall(rhport, p_mtp->ep_out);
|
||||
}
|
||||
if (usbd_edpt_stalled(rhport, p_mtp->ep_in)) {
|
||||
usbd_edpt_clear_stall(rhport, p_mtp->ep_in);
|
||||
}
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
prepare_new_command(p_mtp);
|
||||
return tud_mtp_request_device_reset_cb(&cb_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case MTP_REQ_GET_DEVICE_STATUS: {
|
||||
TU_LOG_DRV(" MTP request: Get Device Status\n");
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
const int32_t len = tud_mtp_request_get_device_status_cb(&cb_data);
|
||||
TU_VERIFY(len > 0);
|
||||
return tud_control_xfer(rhport, request, p_mtp->control_buf, (uint16_t) len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return tud_mtp_request_vendor_cb(&cb_data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Transfer on bulk endpoints
|
||||
bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
if (ep_addr == _mtpd_itf.ep_event) {
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
mtpd_interface_t* p_mtp = &_mtpd_itf;
|
||||
mtp_generic_container_t* p_container = (mtp_generic_container_t*) _mtpd_epbuf.buf;
|
||||
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL
|
||||
tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code);
|
||||
TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code),
|
||||
_mtp_phase_str[p_mtp->phase]);
|
||||
#endif
|
||||
|
||||
const mtp_container_info_t headered_packet = {
|
||||
.header = &p_container->header,
|
||||
.payload = p_container->payload,
|
||||
.payload_bytes = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)
|
||||
};
|
||||
|
||||
const mtp_container_info_t headerless_packet = {
|
||||
.header = &p_mtp->io_header,
|
||||
.payload = _mtpd_epbuf.buf,
|
||||
.payload_bytes = CFG_TUD_MTP_EP_BUFSIZE
|
||||
};
|
||||
|
||||
tud_mtp_cb_data_t cb_data;
|
||||
cb_data.idx = 0;
|
||||
cb_data.phase = p_mtp->phase;
|
||||
cb_data.session_id = p_mtp->session_id;
|
||||
cb_data.command_container = &p_mtp->command;
|
||||
cb_data.io_container = headered_packet;
|
||||
cb_data.total_xferred_bytes = 0;
|
||||
cb_data.xfer_result = event;
|
||||
|
||||
switch (p_mtp->phase) {
|
||||
case MTP_PHASE_COMMAND: {
|
||||
// received new command
|
||||
TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK);
|
||||
memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command
|
||||
p_container->header.len = sizeof(mtp_container_header_t); // default container to header only
|
||||
preprocess_cmd(p_mtp, &cb_data);
|
||||
if (tud_mtp_command_received_cb(&cb_data) < 0) {
|
||||
p_mtp->phase = MTP_PHASE_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MTP_PHASE_DATA: {
|
||||
const uint16_t bulk_mps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64;
|
||||
p_mtp->xferred_len += xferred_bytes;
|
||||
cb_data.total_xferred_bytes = p_mtp->xferred_len;
|
||||
|
||||
bool is_complete = false;
|
||||
// complete if ZLP or short packet or overflow
|
||||
if (xferred_bytes == 0 || // ZLP
|
||||
(xferred_bytes & (bulk_mps - 1)) || // short packet
|
||||
p_mtp->xferred_len > p_mtp->total_len) {
|
||||
is_complete = true;
|
||||
}
|
||||
|
||||
if (ep_addr == p_mtp->ep_in) {
|
||||
// Data In
|
||||
if (is_complete) {
|
||||
cb_data.io_container.header->len = sizeof(mtp_container_header_t);
|
||||
tud_mtp_data_complete_cb(&cb_data);
|
||||
} else {
|
||||
// 2nd+ packet: payload only
|
||||
cb_data.io_container = headerless_packet;
|
||||
tud_mtp_data_xfer_cb(&cb_data);
|
||||
}
|
||||
} else {
|
||||
// Data Out
|
||||
if (p_mtp->xferred_len == xferred_bytes) {
|
||||
// 1st OUT packet: header + payload
|
||||
p_mtp->io_header = p_container->header; // save header for subsequent transaction
|
||||
cb_data.io_container.payload_bytes = xferred_bytes - sizeof(mtp_container_header_t);
|
||||
} else {
|
||||
// 2nd+ packet: payload only
|
||||
cb_data.io_container = headerless_packet;
|
||||
cb_data.io_container.payload_bytes = xferred_bytes;
|
||||
}
|
||||
tud_mtp_data_xfer_cb(&cb_data);
|
||||
|
||||
if (is_complete) {
|
||||
// back to header + payload for response
|
||||
cb_data.io_container = headered_packet;
|
||||
cb_data.io_container.header->len = sizeof(mtp_container_header_t);
|
||||
tud_mtp_data_complete_cb(&cb_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MTP_PHASE_RESPONSE:
|
||||
// response phase is complete -> prepare for new command
|
||||
TU_ASSERT(ep_addr == p_mtp->ep_in);
|
||||
tud_mtp_response_complete_cb(&cb_data);
|
||||
prepare_new_command(p_mtp);
|
||||
break;
|
||||
|
||||
case MTP_PHASE_ERROR:
|
||||
// handled after switch, supposedly to be empty
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
if (p_mtp->phase == MTP_PHASE_ERROR) {
|
||||
// stall both IN & OUT endpoints
|
||||
usbd_edpt_stall(rhport, p_mtp->ep_out);
|
||||
usbd_edpt_stall(rhport, p_mtp->ep_in);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MTPD Internal functionality
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// pre-processed commands
|
||||
void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) {
|
||||
switch (p_mtp->command.header.code) {
|
||||
case MTP_OP_GET_DEVICE_INFO: {
|
||||
tud_mtp_device_info_t dev_info = {
|
||||
.standard_version = 100,
|
||||
.mtp_vendor_extension_id = 6, // MTP specs say 0xFFFFFFFF but libMTP check for value 6
|
||||
.mtp_version = 100,
|
||||
.mtp_extensions = {
|
||||
.count = sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS),
|
||||
.utf16 = { 0 }
|
||||
},
|
||||
.functional_mode = 0x0000,
|
||||
.supported_operations = {
|
||||
.count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS),
|
||||
.arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS }
|
||||
},
|
||||
.supported_events = {
|
||||
.count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS),
|
||||
.arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS }
|
||||
},
|
||||
.supported_device_properties = {
|
||||
.count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES),
|
||||
.arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES }
|
||||
},
|
||||
.capture_formats = {
|
||||
.count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS),
|
||||
.arr = { CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS }
|
||||
},
|
||||
.playback_formats = {
|
||||
.count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS),
|
||||
.arr = { CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS }
|
||||
}
|
||||
};
|
||||
|
||||
for (uint8_t i=0; i < dev_info.mtp_extensions.count; i++) {
|
||||
dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i];
|
||||
}
|
||||
|
||||
mtp_container_add_raw(&cb_data->io_container, &dev_info, sizeof(tud_mtp_device_info_t));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
167
managed_components/espressif__tinyusb/src/class/mtp/mtp_device.h
Normal file
167
managed_components/espressif__tinyusb/src/class/mtp/mtp_device.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com)
|
||||
*
|
||||
* 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 IN0
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MTP_DEVICE_H_
|
||||
#define TUSB_MTP_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "mtp.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_MTP)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// callback data for Bulk Only Transfer (BOT) protocol
|
||||
typedef struct {
|
||||
uint8_t idx; // mtp instance
|
||||
uint8_t phase; // current phase
|
||||
uint32_t session_id;
|
||||
|
||||
const mtp_container_command_t* command_container;
|
||||
mtp_container_info_t io_container;
|
||||
|
||||
tusb_xfer_result_t xfer_result;
|
||||
uint32_t total_xferred_bytes; // number of bytes transferred so far in this phase
|
||||
} tud_mtp_cb_data_t;
|
||||
|
||||
// callback data for Control requests
|
||||
typedef struct {
|
||||
uint8_t idx;
|
||||
uint8_t stage; // control stage
|
||||
uint32_t session_id;
|
||||
|
||||
const tusb_control_request_t* request;
|
||||
// buffer for data stage
|
||||
uint8_t* buf;
|
||||
uint16_t bufsize;
|
||||
} tud_mtp_request_cb_data_t;
|
||||
|
||||
// Number of supported operations, events, device properties, capture formats, playback formats
|
||||
// and max number of characters for strings manufacturer, model, device_version, serial_number
|
||||
#define MTP_DEVICE_INFO_STRUCT(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint16_t standard_version; \
|
||||
uint32_t mtp_vendor_extension_id; \
|
||||
uint16_t mtp_version; \
|
||||
mtp_string_t(_extension_nchars) mtp_extensions; \
|
||||
uint16_t functional_mode; \
|
||||
mtp_auint16_t(_op_count) supported_operations; \
|
||||
mtp_auint16_t(_event_count) supported_events; \
|
||||
mtp_auint16_t(_devprop_count) supported_device_properties; \
|
||||
mtp_auint16_t(_capture_count) capture_formats; \
|
||||
mtp_auint16_t(_playback_count) playback_formats; \
|
||||
/* string fields will be added using append function */ \
|
||||
}
|
||||
|
||||
typedef MTP_DEVICE_INFO_STRUCT(
|
||||
sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS),
|
||||
TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES),
|
||||
TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS)
|
||||
) tud_mtp_device_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// check if mtp interface is mounted
|
||||
bool tud_mtp_mounted(void);
|
||||
|
||||
// send data phase
|
||||
bool tud_mtp_data_send(mtp_container_info_t* p_container);
|
||||
|
||||
// receive data phase
|
||||
bool tud_mtp_data_receive(mtp_container_info_t* p_container);
|
||||
|
||||
// send response
|
||||
bool tud_mtp_response_send(mtp_container_info_t* p_container);
|
||||
|
||||
// send event notification on event endpoint
|
||||
bool tud_mtp_event_send(mtp_event_t* event);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control request Callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received Cancel request. Data is available in callback data's buffer
|
||||
// return false to stall the request
|
||||
bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when received Device Reset request
|
||||
// return false to stall the request
|
||||
bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when received Get Extended Event request. Application fill callback data's buffer for response
|
||||
// return negative to stall the request
|
||||
int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when received Get DeviceStatus request. Application fill callback data's buffer for response
|
||||
// return negative to stall the request
|
||||
int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when received vendor-specific request not in the above standard MTP requests
|
||||
// return false to stall the request
|
||||
bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Bulk only protocol Callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when new command is received. Application fill the cb_data->io_container and call tud_mtp_data_send() or
|
||||
// tud_mtp_response_send() for Data or Response phase.
|
||||
// Return negative to stall the endpoints
|
||||
int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data);
|
||||
|
||||
// Invoked when a data packet is transferred. If data spans over multiple packets, application can use
|
||||
// total_xferred_bytes and io_container's payload_bytes to determine the offset and remaining bytes to be transferred.
|
||||
// Return negative to stall the endpoints
|
||||
int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when all bytes in DATA phase is complete. A response packet is expected
|
||||
// Return negative to stall the endpoints
|
||||
int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data);
|
||||
|
||||
// Invoked when response phase is complete
|
||||
// Return negative to stall the endpoints
|
||||
int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mtpd_init (void);
|
||||
bool mtpd_deinit (void);
|
||||
void mtpd_reset (uint8_t rhport);
|
||||
uint16_t mtpd_open (uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool mtpd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *p_request);
|
||||
bool mtpd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* 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 ( CFG_TUD_ENABLED && CFG_TUD_ECM_RNDIS )
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "net_device.h"
|
||||
#include "rndis_protocol.h"
|
||||
|
||||
extern void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
|
||||
|
||||
#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
|
||||
#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
|
||||
|
||||
#define NETD_PACKET_SIZE (CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN)
|
||||
#define NETD_CONTROL_SIZE 120
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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 receiving 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;
|
||||
|
||||
typedef struct ecm_notify_struct {
|
||||
tusb_control_request_t header;
|
||||
uint32_t downlink, uplink;
|
||||
} ecm_notify_t;
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(rx, NETD_PACKET_SIZE);
|
||||
TUD_EPBUF_DEF(tx, NETD_PACKET_SIZE);
|
||||
|
||||
TUD_EPBUF_DEF(notify, sizeof(ecm_notify_t));
|
||||
TUD_EPBUF_DEF(ctrl, NETD_CONTROL_SIZE);
|
||||
} netd_epbuf_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static netd_interface_t _netd_itf;
|
||||
CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
|
||||
static bool can_xmit;
|
||||
static bool ecm_link_is_up = true; // Store link state for ECM mode
|
||||
|
||||
void tud_network_recv_renew(void) {
|
||||
usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
|
||||
}
|
||||
|
||||
static void do_in_xfer(uint8_t *buf, uint16_t len) {
|
||||
can_xmit = false;
|
||||
usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len);
|
||||
}
|
||||
|
||||
void netd_report(uint8_t *buf, uint16_t len) {
|
||||
const uint8_t rhport = 0;
|
||||
len = tu_min16(len, sizeof(ecm_notify_t));
|
||||
|
||||
if (!usbd_edpt_claim(rhport, _netd_itf.ep_notif)) {
|
||||
TU_LOG1("ECM: Failed to claim notification endpoint\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(_netd_epbuf.notify, buf, len);
|
||||
usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void netd_init(void) {
|
||||
tu_memclr(&_netd_itf, sizeof(_netd_itf));
|
||||
}
|
||||
|
||||
bool netd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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) {
|
||||
ecm_notify_t ecm_notify_nc = {
|
||||
.header = {
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
|
||||
.wValue = ecm_link_is_up ? 1 : 0, /* Use current link state */
|
||||
.wLength = 0,
|
||||
},
|
||||
};
|
||||
|
||||
const ecm_notify_t ecm_notify_csc = {
|
||||
.header = {
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */
|
||||
.wLength = 8,
|
||||
},
|
||||
.downlink = 9728000,
|
||||
.uplink = 9728000,
|
||||
};
|
||||
|
||||
ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc;
|
||||
notify.header.wIndex = _netd_itf.itf_num;
|
||||
netd_report((uint8_t *)¬ify, (nc) ? sizeof(notify.header) : sizeof(notify));
|
||||
}
|
||||
|
||||
// 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 !!
|
||||
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);
|
||||
// Only send connection notification if link is up
|
||||
if (ecm_link_is_up) {
|
||||
ecm_report(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (request->bmRequestType_bit.direction == TUSB_DIR_IN) {
|
||||
rndis_generic_msg_t* rndis_msg = (rndis_generic_msg_t*)((void*)_netd_epbuf.ctrl);
|
||||
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
|
||||
TU_ASSERT(msglen <= NETD_CONTROL_SIZE);
|
||||
tud_control_xfer(rhport, request, _netd_epbuf.ctrl, (uint16_t)msglen);
|
||||
} else {
|
||||
tud_control_xfer(rhport, request, _netd_epbuf.ctrl, NETD_CONTROL_SIZE);
|
||||
}
|
||||
}
|
||||
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(_netd_epbuf.ctrl, request->wLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_incoming_packet(uint32_t len) {
|
||||
uint8_t* pnt = _netd_epbuf.rx;
|
||||
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 = &_netd_epbuf.rx[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
|
||||
size = r->DataLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tud_network_recv_cb(pnt, (uint16_t)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)) {
|
||||
// Notification transfer complete - endpoint is now free
|
||||
// Don't automatically send speed change notification after link state changes
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_network_can_xmit(uint16_t size) {
|
||||
(void)size;
|
||||
return can_xmit;
|
||||
}
|
||||
|
||||
void tud_network_xmit(void *ref, uint16_t arg) {
|
||||
if (!can_xmit) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
|
||||
uint8_t* data = _netd_epbuf.tx + len;
|
||||
|
||||
len += tud_network_xmit_cb(data, ref, arg);
|
||||
|
||||
if (!_netd_itf.ecm_mode) {
|
||||
rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) _netd_epbuf.tx);
|
||||
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(_netd_epbuf.tx, len);
|
||||
}
|
||||
|
||||
// Set the network link state (up/down) and notify the host
|
||||
void tud_network_link_state(uint8_t rhport, bool is_up) {
|
||||
(void)rhport;
|
||||
|
||||
if (_netd_itf.ecm_mode) {
|
||||
ecm_link_is_up = is_up;
|
||||
|
||||
// For ECM mode, send network connection notification only
|
||||
// Don't trigger speed change notification for link state changes
|
||||
ecm_notify_t notify = {
|
||||
.header = {
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0, /* NETWORK_CONNECTION */
|
||||
.wValue = is_up ? 1 : 0, /* 0 = disconnected, 1 = connected */
|
||||
.wLength = 0,
|
||||
},
|
||||
};
|
||||
notify.header.wIndex = _netd_itf.itf_num;
|
||||
netd_report((uint8_t *)¬ify, sizeof(notify.header));
|
||||
} else {
|
||||
// For RNDIS mode, we would need to implement RNDIS status indication
|
||||
// This is more complex and requires RNDIS_INDICATE_STATUS_MSG
|
||||
// For now, RNDIS doesn't support dynamic link state changes
|
||||
(void)is_up;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
164
managed_components/espressif__tinyusb/src/class/net/ncm.h
Normal file
164
managed_components/espressif__tinyusb/src/class/net/ncm.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2024, Hardy Griech
|
||||
*
|
||||
* 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_NCM_H_
|
||||
#define _TUSB_NCM_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
// NTB buffers size for reception side, must be >> MTU to avoid TCP retransmission (driver issue ?)
|
||||
// Linux use 2048 as minimal size
|
||||
#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
|
||||
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
|
||||
#endif
|
||||
|
||||
// NTB buffers size for reception side, must be > MTU
|
||||
// Linux use 2048 as minimal size
|
||||
#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
|
||||
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
|
||||
#endif
|
||||
|
||||
// Number of NTB buffers for reception side
|
||||
// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
|
||||
// On Full-Speed (RP2040) :
|
||||
// 1 - good performance
|
||||
// 2 - up to 30% more performance with iperf with small packets
|
||||
// >2 - no performance gain
|
||||
// On High-Speed (STM32F7) :
|
||||
// No performance gain
|
||||
#ifndef CFG_TUD_NCM_OUT_NTB_N
|
||||
#define CFG_TUD_NCM_OUT_NTB_N 1
|
||||
#endif
|
||||
|
||||
// Number of NTB buffers for transmission side
|
||||
// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
|
||||
// On Full-Speed (RP2040) :
|
||||
// 1 - good performance but SystemView shows lost events (on load test)
|
||||
// 2 - up to 50% more performance with iperf with small packets, "tud_network_can_xmit: request blocked"
|
||||
// happens from time to time with SystemView
|
||||
// 3 - "tud_network_can_xmit: request blocked" never happens
|
||||
// >3 - no performance gain
|
||||
// On High-Speed (STM32F7) :
|
||||
// No performance gain
|
||||
#ifndef CFG_TUD_NCM_IN_NTB_N
|
||||
#define CFG_TUD_NCM_IN_NTB_N 1
|
||||
#endif
|
||||
|
||||
// How many datagrams it is allowed to put into an NTB for transmission side
|
||||
#ifndef CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB
|
||||
#define CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB 8
|
||||
#endif
|
||||
|
||||
// This tells the host how many datagrams it is allowed to put into an NTB
|
||||
#ifndef CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB
|
||||
#define CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB 6
|
||||
#endif
|
||||
|
||||
// Table 6.2 Class-Specific Request Codes for Network Control Model subclass
|
||||
typedef enum
|
||||
{
|
||||
NCM_SET_ETHERNET_MULTICAST_FILTERS = 0x40,
|
||||
NCM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
|
||||
NCM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
|
||||
NCM_SET_ETHERNET_PACKET_FILTER = 0x43,
|
||||
NCM_GET_ETHERNET_STATISTIC = 0x44,
|
||||
NCM_GET_NTB_PARAMETERS = 0x80,
|
||||
NCM_GET_NET_ADDRESS = 0x81,
|
||||
NCM_SET_NET_ADDRESS = 0x82,
|
||||
NCM_GET_NTB_FORMAT = 0x83,
|
||||
NCM_SET_NTB_FORMAT = 0x84,
|
||||
NCM_GET_NTB_INPUT_SIZE = 0x85,
|
||||
NCM_SET_NTB_INPUT_SIZE = 0x86,
|
||||
NCM_GET_MAX_DATAGRAM_SIZE = 0x87,
|
||||
NCM_SET_MAX_DATAGRAM_SIZE = 0x88,
|
||||
NCM_GET_CRC_MODE = 0x89,
|
||||
NCM_SET_CRC_MODE = 0x8A,
|
||||
} ncm_request_code_t;
|
||||
|
||||
#define NTH16_SIGNATURE 0x484D434E
|
||||
#define NDP16_SIGNATURE_NCM0 0x304D434E
|
||||
#define NDP16_SIGNATURE_NCM1 0x314D434E
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint16_t wLength;
|
||||
uint16_t bmNtbFormatsSupported;
|
||||
uint32_t dwNtbInMaxSize;
|
||||
uint16_t wNdbInDivisor;
|
||||
uint16_t wNdbInPayloadRemainder;
|
||||
uint16_t wNdbInAlignment;
|
||||
uint16_t wReserved;
|
||||
uint32_t dwNtbOutMaxSize;
|
||||
uint16_t wNdbOutDivisor;
|
||||
uint16_t wNdbOutPayloadRemainder;
|
||||
uint16_t wNdbOutAlignment;
|
||||
uint16_t wNtbOutMaxDatagrams;
|
||||
} ntb_parameters_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t dwSignature;
|
||||
uint16_t wHeaderLength;
|
||||
uint16_t wSequence;
|
||||
uint16_t wBlockLength;
|
||||
uint16_t wNdpIndex;
|
||||
} nth16_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint16_t wDatagramIndex;
|
||||
uint16_t wDatagramLength;
|
||||
} ndp16_datagram_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint32_t dwSignature;
|
||||
uint16_t wLength;
|
||||
uint16_t wNextNdpIndex;
|
||||
//ndp16_datagram_t datagram[];
|
||||
} ndp16_t;
|
||||
|
||||
typedef union TU_ATTR_PACKED {
|
||||
struct {
|
||||
nth16_t nth;
|
||||
ndp16_t ndp;
|
||||
ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB + 1];
|
||||
};
|
||||
uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
|
||||
} xmit_ntb_t;
|
||||
|
||||
typedef union TU_ATTR_PACKED {
|
||||
struct {
|
||||
nth16_t nth;
|
||||
// only the header is at a guaranteed position
|
||||
};
|
||||
uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
|
||||
} recv_ntb_t;
|
||||
|
||||
typedef struct {
|
||||
tusb_control_request_t header;
|
||||
uint32_t downlink;
|
||||
uint32_t uplink;
|
||||
} ncm_notify_t;
|
||||
|
||||
#endif
|
||||
978
managed_components/espressif__tinyusb/src/class/net/ncm_device.c
Normal file
978
managed_components/espressif__tinyusb/src/class/net/ncm_device.c
Normal file
@@ -0,0 +1,978 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2024 Hardy Griech
|
||||
* Copyright (c) 2020 Jacob Berg Potter
|
||||
* Copyright (c) 2020 Peter Lawrence
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Small Glossary (from the spec)
|
||||
* --------------
|
||||
* Datagram - A collection of bytes forming a single item of information, passed as a unit from source to destination.
|
||||
* NCM - Network Control Model
|
||||
* NDP - NCM Datagram Pointer: NTB structure that delineates Datagrams (typically Ethernet frames) within an NTB
|
||||
* NTB - NCM Transfer Block: a data structure for efficient USB encapsulation of one or more datagrams
|
||||
* Each NTB is designed to be a single USB transfer
|
||||
* NTH - NTB Header: a data structure at the front of each NTB, which provides the information needed to validate
|
||||
* the NTB and begin decoding
|
||||
*
|
||||
* Some explanations
|
||||
* -----------------
|
||||
* - rhport is the USB port of the device, in most cases "0"
|
||||
* - itf_data_alt if != 0 -> data xmit/recv are allowed (see spec)
|
||||
* - ep_in IN endpoints take data from the device intended to go in to the host (the device transmits)
|
||||
* - ep_out OUT endpoints send data out of the host to the device (the device receives)
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "ncm.h"
|
||||
#include "net_device.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUD_NCM_LOG_LEVEL
|
||||
#define CFG_TUD_NCM_LOG_LEVEL CFG_TUD_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
// Alignment must be 4
|
||||
#define TUD_NCM_ALIGNMENT 4
|
||||
// calculate alignment of xmit datagrams within an NTB
|
||||
#define XMIT_ALIGN_OFFSET(x) ((TUD_NCM_ALIGNMENT - ((x) & (TUD_NCM_ALIGNMENT - 1))) & (TUD_NCM_ALIGNMENT - 1))
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Module global things
|
||||
//
|
||||
#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
|
||||
#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
|
||||
|
||||
typedef struct {
|
||||
// general
|
||||
uint8_t ep_in; // endpoint for outgoing datagrams (naming is a little bit confusing)
|
||||
uint8_t ep_out; // endpoint for incoming datagrams (naming is a little bit confusing)
|
||||
uint8_t ep_notif; // endpoint for notifications
|
||||
uint8_t itf_num; // interface number
|
||||
uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
|
||||
uint8_t rhport; // storage of \a rhport because some callbacks are done without it
|
||||
|
||||
// recv handling
|
||||
recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
|
||||
recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic
|
||||
recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver
|
||||
recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic
|
||||
uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
|
||||
|
||||
// xmit handling
|
||||
xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs
|
||||
xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB
|
||||
xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB
|
||||
xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver
|
||||
uint16_t xmit_sequence; // NTB sequence counter
|
||||
uint16_t xmit_glue_ntb_datagram_ndx; // index into \a xmit_glue_ntb_datagram
|
||||
|
||||
// notification handling
|
||||
enum {
|
||||
NOTIFICATION_SPEED,
|
||||
NOTIFICATION_CONNECTED,
|
||||
NOTIFICATION_DONE
|
||||
} notification_xmit_state; // state of notification transmission
|
||||
bool notification_xmit_is_running; // notification is currently transmitted
|
||||
bool link_is_up; // current link state
|
||||
|
||||
// misc
|
||||
bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
|
||||
bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again
|
||||
} ncm_interface_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
TUD_EPBUF_TYPE_DEF(recv_ntb_t, ntb);
|
||||
} recv[RECV_NTB_N];
|
||||
|
||||
struct {
|
||||
TUD_EPBUF_TYPE_DEF(xmit_ntb_t, ntb);
|
||||
} xmit[XMIT_NTB_N];
|
||||
|
||||
TUD_EPBUF_TYPE_DEF(ncm_notify_t, epnotif);
|
||||
} ncm_epbuf_t;
|
||||
|
||||
static ncm_interface_t ncm_interface;
|
||||
CFG_TUD_MEM_SECTION static ncm_epbuf_t ncm_epbuf;
|
||||
|
||||
/**
|
||||
* This is the NTB parameter structure
|
||||
*
|
||||
* \attention
|
||||
* We are lucky, that byte order is correct
|
||||
*/
|
||||
TU_ATTR_ALIGNED(4) static const ntb_parameters_t ntb_parameters = {
|
||||
.wLength = sizeof(ntb_parameters_t),
|
||||
.bmNtbFormatsSupported = 0x01,// 16-bit NTB supported
|
||||
.dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
|
||||
.wNdbInDivisor = 1,
|
||||
.wNdbInPayloadRemainder = 0,
|
||||
.wNdbInAlignment = TUD_NCM_ALIGNMENT,
|
||||
.wReserved = 0,
|
||||
.dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
|
||||
.wNdbOutDivisor = 1,
|
||||
.wNdbOutPayloadRemainder = 0,
|
||||
.wNdbOutAlignment = TUD_NCM_ALIGNMENT,
|
||||
.wNtbOutMaxDatagrams = CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB,
|
||||
};
|
||||
|
||||
// Some confusing remarks about wNtbOutMaxDatagrams...
|
||||
// ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
|
||||
// ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
|
||||
// ==6 -> SystemView runs fine, iperf also
|
||||
// >6 -> iperf starts to show errors
|
||||
// -> 6 seems to be the best value. Why? Don't know, perhaps only on my system?
|
||||
//
|
||||
// iperf: for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
|
||||
// sysview: SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
|
||||
//
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// everything about notifications
|
||||
//
|
||||
|
||||
/**
|
||||
* Transmit next notification to the host (if appropriate).
|
||||
* Notifications are transferred to the host once during connection setup.
|
||||
*/
|
||||
static void notification_xmit(uint8_t rhport, bool force_next) {
|
||||
TU_LOG_DRV("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
|
||||
|
||||
if (!force_next && ncm_interface.notification_xmit_is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
|
||||
TU_LOG_DRV(" NOTIFICATION_SPEED\n");
|
||||
ncm_notify_t notify_speed_change = {
|
||||
.header = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
|
||||
.wValue = 0,
|
||||
.wIndex = ncm_interface.itf_num,
|
||||
.wLength = 8
|
||||
}
|
||||
};
|
||||
if (tud_speed_get() == TUSB_SPEED_HIGH) {
|
||||
notify_speed_change.downlink = 480000000;
|
||||
notify_speed_change.uplink = 480000000;
|
||||
} else {
|
||||
notify_speed_change.downlink = 12000000;
|
||||
notify_speed_change.uplink = 12000000;
|
||||
}
|
||||
|
||||
uint16_t notif_len = sizeof(notify_speed_change.header) + notify_speed_change.header.wLength;
|
||||
ncm_epbuf.epnotif = notify_speed_change;
|
||||
usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_epbuf.epnotif, notif_len);
|
||||
|
||||
ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
|
||||
ncm_interface.notification_xmit_is_running = true;
|
||||
} else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
|
||||
TU_LOG_DRV(" NOTIFICATION_CONNECTED\n");
|
||||
ncm_notify_t notify_connected = {
|
||||
.header = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = CDC_NOTIF_NETWORK_CONNECTION,
|
||||
.wValue = ncm_interface.link_is_up ? 1 : 0, /* Dynamic link state */
|
||||
.wIndex = ncm_interface.itf_num,
|
||||
.wLength = 0,
|
||||
},
|
||||
};
|
||||
|
||||
uint16_t notif_len = sizeof(notify_connected.header) + notify_connected.header.wLength;
|
||||
ncm_epbuf.epnotif = notify_connected;
|
||||
usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_epbuf.epnotif, notif_len);
|
||||
|
||||
ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
|
||||
ncm_interface.notification_xmit_is_running = true;
|
||||
} else {
|
||||
TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
|
||||
ncm_interface.notification_xmit_is_running = false;
|
||||
}
|
||||
} // notification_xmit
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// everything about packet transmission (driver -> TinyUSB)
|
||||
//
|
||||
|
||||
/**
|
||||
* Put NTB into the transmitter free list.
|
||||
*/
|
||||
static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb) {
|
||||
TU_LOG_DRV("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
|
||||
|
||||
if (free_ntb == NULL) { // can happen due to ZLPs
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < XMIT_NTB_N; ++i) {
|
||||
if (ncm_interface.xmit_free_ntb[i] == NULL) {
|
||||
ncm_interface.xmit_free_ntb[i] = free_ntb;
|
||||
return;
|
||||
}
|
||||
}
|
||||
TU_LOG_DRV("(EE) xmit_put_ntb_into_free_list - no entry in free list\n");// this should not happen
|
||||
} // xmit_put_ntb_into_free_list
|
||||
|
||||
/**
|
||||
* Get an NTB from the free list
|
||||
*/
|
||||
static xmit_ntb_t *xmit_get_free_ntb(void) {
|
||||
TU_LOG_DRV("xmit_get_free_ntb()\n");
|
||||
|
||||
for (int i = 0; i < XMIT_NTB_N; ++i) {
|
||||
if (ncm_interface.xmit_free_ntb[i] != NULL) {
|
||||
xmit_ntb_t *free = ncm_interface.xmit_free_ntb[i];
|
||||
ncm_interface.xmit_free_ntb[i] = NULL;
|
||||
return free;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} // xmit_get_free_ntb
|
||||
|
||||
/**
|
||||
* Put a filled NTB into the ready list
|
||||
*/
|
||||
static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
|
||||
TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
|
||||
|
||||
for (int i = 0; i < XMIT_NTB_N; ++i) {
|
||||
if (ncm_interface.xmit_ready_ntb[i] == NULL) {
|
||||
ncm_interface.xmit_ready_ntb[i] = ready_ntb;
|
||||
return;
|
||||
}
|
||||
}
|
||||
TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
|
||||
} // xmit_put_ntb_into_ready_list
|
||||
|
||||
/**
|
||||
* Get the next NTB from the ready list (and remove it from the list).
|
||||
* If the ready list is empty, return NULL.
|
||||
*/
|
||||
static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
|
||||
xmit_ntb_t *r = NULL;
|
||||
|
||||
r = ncm_interface.xmit_ready_ntb[0];
|
||||
memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
|
||||
ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
|
||||
|
||||
TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
|
||||
return r;
|
||||
} // xmit_get_next_ready_ntb
|
||||
|
||||
/**
|
||||
* Transmit a ZLP if required
|
||||
*
|
||||
* \note
|
||||
* Insertion of the ZLPs is a little bit different then described in the spec.
|
||||
* But the below implementation actually works. Don't know if this is a spec
|
||||
* or TinyUSB issue.
|
||||
*
|
||||
* \pre
|
||||
* This must be called from netd_xfer_cb() so that ep_in is ready
|
||||
*/
|
||||
static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
|
||||
TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
|
||||
|
||||
if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
|
||||
TU_ASSERT(!usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
|
||||
|
||||
TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
|
||||
|
||||
// start transmission of the ZLP
|
||||
usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
|
||||
|
||||
return true;
|
||||
} // xmit_insert_required_zlp
|
||||
|
||||
/**
|
||||
* Start transmission if it there is a waiting packet and if can be done from interface side.
|
||||
*/
|
||||
static void xmit_start_if_possible(uint8_t rhport) {
|
||||
TU_LOG_DRV("xmit_start_if_possible()\n");
|
||||
|
||||
if (ncm_interface.xmit_tinyusb_ntb != NULL) {
|
||||
TU_LOG_DRV(" !xmit_start_if_possible 1\n");
|
||||
return;
|
||||
}
|
||||
if (ncm_interface.itf_data_alt != 1) {
|
||||
TU_LOG_DRV("(EE) !xmit_start_if_possible 2\n");
|
||||
return;
|
||||
}
|
||||
if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
|
||||
TU_LOG_DRV(" !xmit_start_if_possible 3\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ncm_interface.xmit_tinyusb_ntb = xmit_get_next_ready_ntb();
|
||||
if (ncm_interface.xmit_tinyusb_ntb == NULL) {
|
||||
if (ncm_interface.xmit_glue_ntb == NULL || ncm_interface.xmit_glue_ntb_datagram_ndx == 0) {
|
||||
// -> really nothing is waiting
|
||||
return;
|
||||
}
|
||||
ncm_interface.xmit_tinyusb_ntb = ncm_interface.xmit_glue_ntb;
|
||||
ncm_interface.xmit_glue_ntb = NULL;
|
||||
}
|
||||
|
||||
#if CFG_TUD_NCM_LOG_LEVEL >= 3
|
||||
{
|
||||
uint16_t len = ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength;
|
||||
TU_LOG_BUF(3, ncm_interface.xmit_tinyusb_ntb->data[i], len);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ncm_interface.xmit_glue_ntb_datagram_ndx != 1) {
|
||||
TU_LOG_DRV(">> %d %d\n", ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength, ncm_interface.xmit_glue_ntb_datagram_ndx);
|
||||
}
|
||||
|
||||
// Kick off an endpoint transfer
|
||||
usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
|
||||
} // xmit_start_if_possible
|
||||
|
||||
/**
|
||||
* check if a new datagram fits into the current NTB
|
||||
*/
|
||||
static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size) {
|
||||
TU_LOG_DRV("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
|
||||
|
||||
if (ncm_interface.xmit_glue_ntb == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
|
||||
return false;
|
||||
}
|
||||
if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_IN_NTB_MAX_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // xmit_requested_datagram_fits_into_current_ntb
|
||||
|
||||
/**
|
||||
* Setup an NTB for the glue logic
|
||||
*/
|
||||
static bool xmit_setup_next_glue_ntb(void) {
|
||||
TU_LOG_DRV("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
|
||||
|
||||
if (ncm_interface.xmit_glue_ntb != NULL) {
|
||||
// put NTB into waiting list (the new datagram did not fit in)
|
||||
xmit_put_ntb_into_ready_list(ncm_interface.xmit_glue_ntb);
|
||||
}
|
||||
|
||||
ncm_interface.xmit_glue_ntb = xmit_get_free_ntb();// get next buffer (if any)
|
||||
if (ncm_interface.xmit_glue_ntb == NULL) {
|
||||
TU_LOG_DRV(" xmit_setup_next_glue_ntb - nothing free\n");// should happen rarely
|
||||
return false;
|
||||
}
|
||||
|
||||
ncm_interface.xmit_glue_ntb_datagram_ndx = 0;
|
||||
|
||||
xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
|
||||
|
||||
// Fill in NTB header
|
||||
ntb->nth.dwSignature = NTH16_SIGNATURE;
|
||||
ntb->nth.wHeaderLength = sizeof(ntb->nth);
|
||||
ntb->nth.wSequence = ncm_interface.xmit_sequence++;
|
||||
ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
|
||||
ntb->nth.wNdpIndex = sizeof(ntb->nth);
|
||||
|
||||
// Fill in NDP16 header and terminator
|
||||
ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
|
||||
ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
|
||||
ntb->ndp.wNextNdpIndex = 0;
|
||||
|
||||
memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
|
||||
return true;
|
||||
} // xmit_setup_next_glue_ntb
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
|
||||
//
|
||||
|
||||
/**
|
||||
* Return pointer to an available receive buffer or NULL.
|
||||
* Returned buffer (if any) has the size \a CFG_TUD_NCM_OUT_NTB_MAX_SIZE.
|
||||
*/
|
||||
static recv_ntb_t *recv_get_free_ntb(void) {
|
||||
TU_LOG_DRV("recv_get_free_ntb()\n");
|
||||
|
||||
for (int i = 0; i < RECV_NTB_N; ++i) {
|
||||
if (ncm_interface.recv_free_ntb[i] != NULL) {
|
||||
recv_ntb_t *free = ncm_interface.recv_free_ntb[i];
|
||||
ncm_interface.recv_free_ntb[i] = NULL;
|
||||
return free;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} // recv_get_free_ntb
|
||||
|
||||
/**
|
||||
* Get the next NTB from the ready list (and remove it from the list).
|
||||
* If the ready list is empty, return NULL.
|
||||
*/
|
||||
static recv_ntb_t *recv_get_next_ready_ntb(void) {
|
||||
recv_ntb_t *r = NULL;
|
||||
|
||||
r = ncm_interface.recv_ready_ntb[0];
|
||||
memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
|
||||
ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
|
||||
|
||||
TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
|
||||
return r;
|
||||
} // recv_get_next_ready_ntb
|
||||
|
||||
/**
|
||||
* Put NTB into the receiver free list.
|
||||
*/
|
||||
static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) {
|
||||
TU_LOG_DRV("recv_put_ntb_into_free_list(%p)\n", free_ntb);
|
||||
|
||||
for (int i = 0; i < RECV_NTB_N; ++i) {
|
||||
if (ncm_interface.recv_free_ntb[i] == NULL) {
|
||||
ncm_interface.recv_free_ntb[i] = free_ntb;
|
||||
return;
|
||||
}
|
||||
}
|
||||
TU_LOG_DRV("(EE) recv_put_ntb_into_free_list - no entry in free list\n");// this should not happen
|
||||
} // recv_put_ntb_into_free_list
|
||||
|
||||
/**
|
||||
* \a ready_ntb holds a validated NTB,
|
||||
* put this buffer into the waiting list.
|
||||
*/
|
||||
static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) {
|
||||
TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
|
||||
|
||||
for (int i = 0; i < RECV_NTB_N; ++i) {
|
||||
if (ncm_interface.recv_ready_ntb[i] == NULL) {
|
||||
ncm_interface.recv_ready_ntb[i] = ready_ntb;
|
||||
return;
|
||||
}
|
||||
}
|
||||
TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
|
||||
} // recv_put_ntb_into_ready_list
|
||||
|
||||
/**
|
||||
* If possible, start a new reception TinyUSB -> driver.
|
||||
*/
|
||||
static void recv_try_to_start_new_reception(uint8_t rhport) {
|
||||
TU_LOG_DRV("recv_try_to_start_new_reception(%d)\n", rhport);
|
||||
|
||||
if (ncm_interface.itf_data_alt != 1) {
|
||||
return;
|
||||
}
|
||||
if (ncm_interface.recv_tinyusb_ntb != NULL) {
|
||||
return;
|
||||
}
|
||||
if (usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ncm_interface.recv_tinyusb_ntb = recv_get_free_ntb();
|
||||
if (ncm_interface.recv_tinyusb_ntb == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initiate transfer
|
||||
TU_LOG_DRV(" start reception\n");
|
||||
bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
|
||||
if (!r) {
|
||||
recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
|
||||
ncm_interface.recv_tinyusb_ntb = NULL;
|
||||
}
|
||||
} // recv_try_to_start_new_reception
|
||||
|
||||
/**
|
||||
* Validate incoming datagram.
|
||||
* \return true if valid
|
||||
*
|
||||
* \note
|
||||
* \a ndp16->wNextNdpIndex != 0 is not supported
|
||||
*/
|
||||
static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) {
|
||||
const nth16_t *nth16 = &(ntb->nth);
|
||||
|
||||
TU_LOG_DRV("recv_validate_datagram(%p, %d)\n", ntb, (int) len);
|
||||
|
||||
// check header
|
||||
if (nth16->wHeaderLength != sizeof(nth16_t)) {
|
||||
TU_LOG_DRV("(EE) ill nth16 length: %d\n", nth16->wHeaderLength);
|
||||
return false;
|
||||
}
|
||||
if (nth16->dwSignature != NTH16_SIGNATURE) {
|
||||
TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) nth16->dwSignature);
|
||||
return false;
|
||||
}
|
||||
if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
|
||||
TU_LOG_DRV("(EE) ill min len: %lu\n", len);
|
||||
return false;
|
||||
}
|
||||
if (nth16->wBlockLength > len) {
|
||||
TU_LOG_DRV("(EE) ill block length: %d > %lu\n", nth16->wBlockLength, len);
|
||||
return false;
|
||||
}
|
||||
if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
|
||||
TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
|
||||
return false;
|
||||
}
|
||||
if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) {
|
||||
TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check (first) NDP(16)
|
||||
const ndp16_t *ndp16 = (const ndp16_t *) (ntb->data + nth16->wNdpIndex);
|
||||
|
||||
if (ndp16->wLength < sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
|
||||
TU_LOG_DRV("(EE) ill ndp16 length: %d\n", ndp16->wLength);
|
||||
return false;
|
||||
}
|
||||
if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
|
||||
TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) ndp16->dwSignature);
|
||||
return false;
|
||||
}
|
||||
if (ndp16->wNextNdpIndex != 0) {
|
||||
TU_LOG_DRV("(EE) cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ndp16_datagram_t *ndp16_datagram = (const ndp16_datagram_t *) (ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
|
||||
int ndx = 0;
|
||||
uint16_t max_ndx = (uint16_t) ((ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t));
|
||||
|
||||
if (max_ndx > 2) { // number of datagrams in NTB > 1
|
||||
TU_LOG_DRV("<< %d (%d)\n", max_ndx - 1, ntb->nth.wBlockLength);
|
||||
}
|
||||
if (ndp16_datagram[max_ndx - 1].wDatagramIndex != 0 || ndp16_datagram[max_ndx - 1].wDatagramLength != 0) {
|
||||
TU_LOG_DRV(" max_ndx != 0\n");
|
||||
return false;
|
||||
}
|
||||
while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
|
||||
TU_LOG_DRV(" << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
|
||||
if (ndp16_datagram[ndx].wDatagramIndex > len) {
|
||||
TU_LOG_DRV("(EE) ill start of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
|
||||
return false;
|
||||
}
|
||||
if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
|
||||
TU_LOG_DRV("(EE) ill end of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
|
||||
return false;
|
||||
}
|
||||
++ndx;
|
||||
}
|
||||
|
||||
#if CFG_TUD_NCM_LOG_LEVEL >= 3
|
||||
TU_LOG_BUF(3, ntb->data[i], len);
|
||||
#endif
|
||||
|
||||
// -> ntb contains a valid packet structure
|
||||
// ok... I did not check for garbage within the datagram indices...
|
||||
return true;
|
||||
} // recv_validate_datagram
|
||||
|
||||
/**
|
||||
* Transfer the next (pending) datagram to the glue logic and return receive buffer if empty.
|
||||
*/
|
||||
static void recv_transfer_datagram_to_glue_logic(void) {
|
||||
TU_LOG_DRV("recv_transfer_datagram_to_glue_logic()\n");
|
||||
|
||||
if (ncm_interface.recv_glue_ntb == NULL) {
|
||||
ncm_interface.recv_glue_ntb = recv_get_next_ready_ntb();
|
||||
TU_LOG_DRV(" new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
|
||||
ncm_interface.recv_glue_ntb_datagram_ndx = 0;
|
||||
}
|
||||
|
||||
if (ncm_interface.recv_glue_ntb != NULL) {
|
||||
const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *) (ncm_interface.recv_glue_ntb->data + ncm_interface.recv_glue_ntb->nth.wNdpIndex + sizeof(ndp16_t));
|
||||
|
||||
if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex == 0) {
|
||||
TU_LOG_DRV("(EE) SOMETHING WENT WRONG 1\n");
|
||||
} else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
|
||||
TU_LOG_DRV("(EE) SOMETHING WENT WRONG 2\n");
|
||||
} else {
|
||||
uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
|
||||
uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
|
||||
|
||||
TU_LOG_DRV(" recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
|
||||
if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
|
||||
// send datagram successfully to glue logic
|
||||
TU_LOG_DRV(" OK\n");
|
||||
datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
|
||||
datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
|
||||
|
||||
if (datagramIndex != 0 && datagramLength != 0) {
|
||||
// -> next datagram
|
||||
++ncm_interface.recv_glue_ntb_datagram_ndx;
|
||||
} else {
|
||||
// end of datagrams reached
|
||||
recv_put_ntb_into_free_list(ncm_interface.recv_glue_ntb);
|
||||
ncm_interface.recv_glue_ntb = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // recv_transfer_datagram_to_glue_logic
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// all the tud_network_*() stuff (glue logic -> driver)
|
||||
//
|
||||
|
||||
/**
|
||||
* Check if the glue logic is allowed to call tud_network_xmit().
|
||||
* This function also fetches a next buffer if required, so that tud_network_xmit() is ready for copy
|
||||
* and transmission operation.
|
||||
*/
|
||||
bool tud_network_can_xmit(uint16_t size) {
|
||||
TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
|
||||
|
||||
TU_ASSERT(size <= CFG_TUD_NCM_IN_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
|
||||
|
||||
if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
|
||||
// -> everything is fine
|
||||
return true;
|
||||
}
|
||||
xmit_start_if_possible(ncm_interface.rhport);
|
||||
TU_LOG_DRV("(II) tud_network_can_xmit: request blocked\n");// could happen if all xmit buffers are full (but should happen rarely)
|
||||
return false;
|
||||
} // tud_network_can_xmit
|
||||
|
||||
/**
|
||||
* Put a datagram into a waiting NTB.
|
||||
* If currently no transmission is started, then initiate transmission.
|
||||
*/
|
||||
void tud_network_xmit(void *ref, uint16_t arg) {
|
||||
TU_LOG_DRV("tud_network_xmit(%p, %d)\n", ref, arg);
|
||||
|
||||
if (ncm_interface.xmit_glue_ntb == NULL) {
|
||||
TU_LOG_DRV("(EE) tud_network_xmit: no buffer\n");// must not happen (really)
|
||||
return;
|
||||
}
|
||||
|
||||
xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
|
||||
|
||||
// copy new datagram to the end of the current NTB
|
||||
uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
|
||||
|
||||
// correct NTB internals
|
||||
ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramIndex = ntb->nth.wBlockLength;
|
||||
ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramLength = size;
|
||||
ncm_interface.xmit_glue_ntb_datagram_ndx += 1;
|
||||
|
||||
ntb->nth.wBlockLength += (uint16_t) (size + XMIT_ALIGN_OFFSET(size));
|
||||
|
||||
if (ntb->nth.wBlockLength > CFG_TUD_NCM_IN_NTB_MAX_SIZE) {
|
||||
TU_LOG_DRV("(EE) tud_network_xmit: buffer overflow\n"); // must not happen (really)
|
||||
return;
|
||||
}
|
||||
|
||||
xmit_start_if_possible(ncm_interface.rhport);
|
||||
} // tud_network_xmit
|
||||
|
||||
/**
|
||||
* Keep the receive logic busy and transfer pending packets to the glue logic.
|
||||
* Avoid recursive calls due to wrong expectations of the net glue logic,
|
||||
* see https://github.com/hathach/tinyusb/issues/2711
|
||||
*/
|
||||
void tud_network_recv_renew(void) {
|
||||
TU_LOG_DRV("tud_network_recv_renew()\n");
|
||||
|
||||
ncm_interface.tud_network_recv_renew_process_again = true;
|
||||
|
||||
if (ncm_interface.tud_network_recv_renew_active) {
|
||||
TU_LOG_DRV("Re-entrant into tud_network_recv_renew, will process later\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (ncm_interface.tud_network_recv_renew_process_again) {
|
||||
ncm_interface.tud_network_recv_renew_process_again = false;
|
||||
|
||||
// If the current function is called within recv_transfer_datagram_to_glue_logic,
|
||||
// tud_network_recv_renew_process_again will become true, and the loop will run again
|
||||
// Otherwise the loop will not run again
|
||||
ncm_interface.tud_network_recv_renew_active = true;
|
||||
recv_transfer_datagram_to_glue_logic();
|
||||
ncm_interface.tud_network_recv_renew_active = false;
|
||||
}
|
||||
recv_try_to_start_new_reception(ncm_interface.rhport);
|
||||
} // tud_network_recv_renew
|
||||
|
||||
/**
|
||||
* Same as tud_network_recv_renew() but knows \a rhport
|
||||
*/
|
||||
static void tud_network_recv_renew_r(uint8_t rhport) {
|
||||
TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
|
||||
|
||||
ncm_interface.rhport = rhport;
|
||||
tud_network_recv_renew();
|
||||
} // tud_network_recv_renew
|
||||
|
||||
/**
|
||||
* Set the link state and send notification to host
|
||||
*/
|
||||
void tud_network_link_state(uint8_t rhport, bool is_up) {
|
||||
TU_LOG_DRV("tud_network_link_state(%d, %d)\n", rhport, is_up);
|
||||
|
||||
if (ncm_interface.link_is_up == is_up) {
|
||||
// No change in link state
|
||||
return;
|
||||
}
|
||||
|
||||
ncm_interface.link_is_up = is_up;
|
||||
|
||||
// Only send notification if we have an active data interface
|
||||
if (ncm_interface.itf_data_alt != 1) {
|
||||
TU_LOG_DRV(" link state notification skipped (interface not active)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset notification state to send link state update
|
||||
ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
|
||||
|
||||
// Trigger notification transmission
|
||||
notification_xmit(rhport, false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// all the netd_*() stuff (interface TinyUSB -> driver)
|
||||
//
|
||||
/**
|
||||
* Initialize the driver data structures.
|
||||
* Might be called several times.
|
||||
*/
|
||||
void netd_init(void) {
|
||||
TU_LOG_DRV("netd_init()\n");
|
||||
|
||||
memset(&ncm_interface, 0, sizeof(ncm_interface));
|
||||
|
||||
for (int i = 0; i < XMIT_NTB_N; ++i) {
|
||||
ncm_interface.xmit_free_ntb[i] = &ncm_epbuf.xmit[i].ntb;
|
||||
}
|
||||
for (int i = 0; i < RECV_NTB_N; ++i) {
|
||||
ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
|
||||
}
|
||||
// Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP
|
||||
#ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
|
||||
ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP;
|
||||
#else
|
||||
ncm_interface.link_is_up = true; // Default to link up if not set.
|
||||
#endif
|
||||
} // netd_init
|
||||
|
||||
/**
|
||||
* Deinit driver
|
||||
*/
|
||||
bool netd_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the port.
|
||||
* In this driver this is the same as netd_init()
|
||||
*/
|
||||
void netd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
netd_init();
|
||||
} // netd_reset
|
||||
|
||||
/**
|
||||
* Open the USB interface.
|
||||
* - parse the USB descriptor \a TUD_CDC_NCM_DESCRIPTOR for itfnum and endpoints
|
||||
* - a specific order of elements in the descriptor is tested.
|
||||
*
|
||||
* \note
|
||||
* Actually all of the information could be read directly from \a itf_desc, because the
|
||||
* structure and the values are well known. But we do it this way.
|
||||
*
|
||||
* \post
|
||||
* - \a itf_num set
|
||||
* - \a ep_notif, \a ep_in and \a ep_out are set
|
||||
* - USB interface is open
|
||||
*/
|
||||
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
|
||||
TU_ASSERT(ncm_interface.ep_notif == 0, 0);// assure that the interface is only opened once
|
||||
|
||||
ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
|
||||
|
||||
// skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
|
||||
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||
uint8_t const *p_desc = tu_desc_next(itf_desc);
|
||||
while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// get notification endpoint
|
||||
TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
|
||||
TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
|
||||
ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
// skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
|
||||
while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
|
||||
tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const *) p_desc;
|
||||
TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
|
||||
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
|
||||
TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
|
||||
drv_len += 2 * sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
return drv_len;
|
||||
} // netd_open
|
||||
|
||||
/**
|
||||
* Handle TinyUSB requests to process transfer events.
|
||||
*/
|
||||
bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
if (ep_addr == ncm_interface.ep_out) {
|
||||
// new NTB received
|
||||
// - make the NTB valid
|
||||
// - if ready transfer datagrams to the glue logic for further processing
|
||||
// - if there is a free receive buffer, initiate reception
|
||||
if (!recv_validate_datagram(ncm_interface.recv_tinyusb_ntb, xferred_bytes)) {
|
||||
// verification failed: ignore NTB and return it to free
|
||||
TU_LOG_DRV("Invalid datatagram. Ignoring NTB\n");
|
||||
recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
|
||||
} else {
|
||||
// packet ok -> put it into ready list
|
||||
recv_put_ntb_into_ready_list(ncm_interface.recv_tinyusb_ntb);
|
||||
}
|
||||
ncm_interface.recv_tinyusb_ntb = NULL;
|
||||
tud_network_recv_renew_r(rhport);
|
||||
} else if (ep_addr == ncm_interface.ep_in) {
|
||||
// transmission of an NTB finished
|
||||
// - free the transmitted NTB buffer
|
||||
// - insert ZLPs when necessary
|
||||
// - if there is another transmit NTB waiting, try to start transmission
|
||||
xmit_put_ntb_into_free_list(ncm_interface.xmit_tinyusb_ntb);
|
||||
ncm_interface.xmit_tinyusb_ntb = NULL;
|
||||
if (!xmit_insert_required_zlp(rhport, xferred_bytes)) {
|
||||
xmit_start_if_possible(rhport);
|
||||
}
|
||||
} else if (ep_addr == ncm_interface.ep_notif) {
|
||||
// next transfer on notification channel
|
||||
notification_xmit(rhport, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
} // netd_xfer_cb
|
||||
|
||||
/**
|
||||
* Respond to TinyUSB control requests.
|
||||
* At startup transmission of notification packets are done here.
|
||||
*/
|
||||
bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (request->bmRequestType_bit.type) {
|
||||
case TUSB_REQ_TYPE_STANDARD:
|
||||
|
||||
switch (request->bRequest) {
|
||||
case TUSB_REQ_GET_INTERFACE: {
|
||||
TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
|
||||
|
||||
tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
|
||||
} break;
|
||||
|
||||
case TUSB_REQ_SET_INTERFACE: {
|
||||
TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
|
||||
|
||||
ncm_interface.itf_data_alt = (uint8_t) request->wValue;
|
||||
|
||||
if (ncm_interface.itf_data_alt == 1) {
|
||||
tud_network_recv_renew_r(rhport);
|
||||
notification_xmit(rhport, false);
|
||||
}
|
||||
tud_control_status(rhport, request);
|
||||
} break;
|
||||
|
||||
// unsupported request
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_REQ_TYPE_CLASS:
|
||||
TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
|
||||
switch (request->bRequest) {
|
||||
case NCM_GET_NTB_PARAMETERS: {
|
||||
// transfer NTB parameters to host.
|
||||
tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
|
||||
} break;
|
||||
|
||||
// unsupported request
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
// unsupported request
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} // netd_control_xfer_cb
|
||||
|
||||
#endif // ( CFG_TUD_ENABLED && CFG_TUD_NCM )
|
||||
110
managed_components/espressif__tinyusb/src/class/net/net_device.h
Normal file
110
managed_components/espressif__tinyusb/src/class/net/net_device.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include "class/cdc/cdc.h"
|
||||
|
||||
#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
|
||||
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
|
||||
#endif
|
||||
|
||||
/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
|
||||
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */
|
||||
#ifndef CFG_TUD_NET_MTU
|
||||
#define CFG_TUD_NET_MTU 1514
|
||||
#endif
|
||||
|
||||
|
||||
// Table 4.3 Data Class Interface Protocol Codes
|
||||
typedef enum
|
||||
{
|
||||
NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
|
||||
} ncm_data_interface_protocol_code_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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(uint16_t size);
|
||||
|
||||
// if network_can_xmit() returns true, network_xmit() can be called once
|
||||
void tud_network_xmit(void *ref, uint16_t arg);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callbacks (WEAK is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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);
|
||||
|
||||
//------------- ECM/RNDIS -------------//
|
||||
|
||||
// client must provide this: initialize any network state back to the beginning
|
||||
void tud_network_init_cb(void);
|
||||
|
||||
// client must provide this: 48-bit MAC address
|
||||
// TODO removed later since it is not part of tinyusb stack
|
||||
extern uint8_t tud_network_mac_address[6];
|
||||
|
||||
//------------- NCM -------------//
|
||||
|
||||
// Set the network link state (up/down) and notify the host
|
||||
void tud_network_link_state(uint8_t rhport, bool is_up);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void netd_init (void);
|
||||
bool netd_deinit (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_ */
|
||||
343
managed_components/espressif__tinyusb/src/class/usbtmc/usbtmc.h
Normal file
343
managed_components/espressif__tinyusb/src/class/usbtmc/usbtmc.h
Normal file
@@ -0,0 +1,343 @@
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 N Conrad
|
||||
*
|
||||
* 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_USBTMC_H__
|
||||
#define _TUSB_USBTMC_H__
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
|
||||
/* Implements USBTMC Revision 1.0, April 14, 2003
|
||||
|
||||
String descriptors must have a "LANGID=0x409"/US English string.
|
||||
Characters must be 0x20 (' ') to 0x7E ('~') ASCII,
|
||||
But MUST not contain: "/:?\*
|
||||
Also must not have leading or trailing space (' ')
|
||||
Device descriptor must state USB version 0x0200 or greater
|
||||
|
||||
If USB488DeviceCapabilites.D2 = 1 (SR1), then there must be a INT endpoint.
|
||||
*/
|
||||
|
||||
#define USBTMC_VERSION 0x0100
|
||||
#define USBTMC_488_VERSION 0x0100
|
||||
|
||||
typedef enum {
|
||||
USBTMC_MSGID_DEV_DEP_MSG_OUT = 1u,
|
||||
USBTMC_MSGID_DEV_DEP_MSG_IN = 2u,
|
||||
USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT = 126u,
|
||||
USBTMC_MSGID_VENDOR_SPECIFIC_IN = 127u,
|
||||
USBTMC_MSGID_USB488_TRIGGER = 128u,
|
||||
} usbtmc_msgid_enum;
|
||||
|
||||
/// \brief Message header (For BULK OUT and BULK IN); 4 bytes
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t MsgID ; ///< Message type ID (usbtmc_msgid_enum)
|
||||
uint8_t bTag ; ///< Transfer ID 1<=bTag<=255
|
||||
uint8_t bTagInverse ; ///< Complement of the tag
|
||||
uint8_t _reserved ; ///< Must be 0x00
|
||||
} usbtmc_msg_header_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
usbtmc_msg_header_t header;
|
||||
uint8_t data[8];
|
||||
} usbtmc_msg_generic_t;
|
||||
|
||||
/* Uses on the bulk-out endpoint: */
|
||||
// Next 8 bytes are message-specific
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
usbtmc_msg_header_t header ; ///< Header
|
||||
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int EOM : 1 ; ///< EOM set on last byte
|
||||
} bmTransferAttributes;
|
||||
uint8_t _reserved[3];
|
||||
} usbtmc_msg_request_dev_dep_out;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_out) == 12u, "struct wrong length");
|
||||
|
||||
// Next 8 bytes are message-specific
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
usbtmc_msg_header_t header ; ///< Header
|
||||
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int TermCharEnabled : 1 ; ///< "The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
|
||||
} bmTransferAttributes;
|
||||
uint8_t TermChar;
|
||||
uint8_t _reserved[2];
|
||||
} usbtmc_msg_request_dev_dep_in;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_in) == 12u, "struct wrong length");
|
||||
|
||||
/* Bulk-in headers */
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
usbtmc_msg_header_t header;
|
||||
uint32_t TransferSize;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t EOM: 1; ///< Last byte of transfer is the end of the message
|
||||
uint8_t UsingTermChar: 1; ///< Support TermChar && Request.TermCharEnabled && last char in transfer is TermChar
|
||||
} bmTransferAttributes;
|
||||
uint8_t _reserved[3];
|
||||
} usbtmc_msg_dev_dep_msg_in_header_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_msg_dev_dep_msg_in_header_t) == 12u, "struct wrong length");
|
||||
|
||||
/* Unsupported vendor things.... Are these ever used?*/
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
usbtmc_msg_header_t header ; ///< Header
|
||||
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||
uint8_t _reserved[4];
|
||||
} usbtmc_msg_request_vendor_specific_out;
|
||||
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_out) == 12u, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
usbtmc_msg_header_t header ; ///< Header
|
||||
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||
uint8_t _reserved[4];
|
||||
} usbtmc_msg_request_vendor_specific_in;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_in) == 12u, "struct wrong length");
|
||||
|
||||
// Control request type should use tusb_control_request_t
|
||||
|
||||
/*
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
struct {
|
||||
unsigned int Recipient : 5 ; ///< EOM set on last byte
|
||||
unsigned int Type : 2 ; ///< EOM set on last byte
|
||||
unsigned int DirectionToHost : 1 ; ///< 0 is OUT, 1 is IN
|
||||
} bmRequestType;
|
||||
uint8_t bRequest ; ///< If bmRequestType.Type = Class, see usmtmc_request_type_enum
|
||||
uint16_t wValue ;
|
||||
uint16_t wIndex ;
|
||||
uint16_t wLength ; // Number of bytes in data stage
|
||||
} usbtmc_class_specific_control_req;
|
||||
|
||||
*/
|
||||
// bulk-in protocol errors
|
||||
enum {
|
||||
USBTMC_BULK_IN_ERR_INCOMPLETE_HEADER = 1u,
|
||||
USBTMC_BULK_IN_ERR_UNSUPPORTED = 2u,
|
||||
USBTMC_BULK_IN_ERR_BAD_PARAMETER = 3u,
|
||||
USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u,
|
||||
USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u,
|
||||
};
|
||||
// built-in halt errors
|
||||
enum {
|
||||
USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a
|
||||
/// Bulk-IN transfer is in progress
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT = 1u,
|
||||
USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS = 2u,
|
||||
USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN = 3u,
|
||||
USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS = 4u,
|
||||
USBTMC_bREQUEST_INITIATE_CLEAR = 5u,
|
||||
USBTMC_bREQUEST_CHECK_CLEAR_STATUS = 6u,
|
||||
USBTMC_bREQUEST_GET_CAPABILITIES = 7u,
|
||||
|
||||
USBTMC_bREQUEST_INDICATOR_PULSE = 64u, // Optional
|
||||
|
||||
/****** USBTMC 488 *************/
|
||||
USB488_bREQUEST_READ_STATUS_BYTE = 128u,
|
||||
USB488_bREQUEST_REN_CONTROL = 160u,
|
||||
USB488_bREQUEST_GO_TO_LOCAL = 161u,
|
||||
USB488_bREQUEST_LOCAL_LOCKOUT = 162u,
|
||||
|
||||
} usmtmc_request_type_enum;
|
||||
|
||||
typedef enum {
|
||||
// The last and first valid bNotify1 for use by the USBTMC class specification.
|
||||
USBTMC_bNOTIFY1_USBTMC_FIRST = 0x00,
|
||||
USBTMC_bNOTIFY1_USBTMC_LAST = 0x3F,
|
||||
|
||||
// The last and first valid bNotify1 for use by vendors.
|
||||
USBTMC_bNOTIFY1_VENDOR_SPECIFIC_FIRST = 0x40,
|
||||
USBTMC_bNOTIFY1_VENDOR_SPECIFIC_LAST = 0x7F,
|
||||
|
||||
// The last and first valid bNotify1 for use by USBTMC subclass specifications.
|
||||
USBTMC_bNOTIFY1_SUBCLASS_FIRST = 0x80,
|
||||
USBTMC_bNOTIFY1_SUBCLASS_LAST = 0xFF,
|
||||
|
||||
// From the USB488 Subclass Specification, Section 3.4.
|
||||
USB488_bNOTIFY1_SRQ = 0x81,
|
||||
} usbtmc_int_in_payload_format;
|
||||
|
||||
typedef enum {
|
||||
USBTMC_STATUS_SUCCESS = 0x01,
|
||||
USBTMC_STATUS_PENDING = 0x02,
|
||||
USBTMC_STATUS_FAILED = 0x80,
|
||||
USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS = 0x81,
|
||||
USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS = 0x82,
|
||||
USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83,
|
||||
|
||||
/****** USBTMC 488 *************/
|
||||
USB488_STATUS_INTERRUPT_IN_BUSY = 0x20
|
||||
} usbtmc_status_enum;
|
||||
|
||||
/************************************************************
|
||||
* Control Responses
|
||||
*/
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t USBTMC_status; ///< usbtmc_status_enum
|
||||
uint8_t _reserved;
|
||||
uint16_t bcdUSBTMC; ///< USBTMC_VERSION
|
||||
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int listenOnly :1;
|
||||
unsigned int talkOnly :1;
|
||||
unsigned int supportsIndicatorPulse :1;
|
||||
} bmIntfcCapabilities;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int canEndBulkInOnTermChar :1;
|
||||
} bmDevCapabilities;
|
||||
uint8_t _reserved2[6];
|
||||
uint8_t _reserved3[12];
|
||||
} usbtmc_response_capabilities_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t USBTMC_status;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int BulkInFifoBytes :1;
|
||||
} bmClear;
|
||||
} usbtmc_get_clear_status_rsp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
|
||||
|
||||
// Used for both abort bulk IN and bulk OUT
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t USBTMC_status;
|
||||
uint8_t bTag;
|
||||
} usbtmc_initiate_abort_rsp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
|
||||
|
||||
// Used for both check_abort_bulk_in_status and check_abort_bulk_out_status
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t USBTMC_status;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int BulkInFifoBytes : 1; ///< Has queued data or a short packet that is queued
|
||||
} bmAbortBulkIn;
|
||||
uint8_t _reserved[2]; ///< Must be zero
|
||||
uint32_t NBYTES_RXD_TXD;
|
||||
} usbtmc_check_abort_bulk_rsp_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_check_abort_bulk_rsp_t) == 8u, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t USBTMC_status; ///< usbtmc_status_enum
|
||||
uint8_t _reserved;
|
||||
uint16_t bcdUSBTMC; ///< USBTMC_VERSION
|
||||
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t listenOnly :1;
|
||||
uint8_t talkOnly :1;
|
||||
uint8_t supportsIndicatorPulse :1;
|
||||
} bmIntfcCapabilities;
|
||||
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t canEndBulkInOnTermChar :1;
|
||||
} bmDevCapabilities;
|
||||
|
||||
uint8_t _reserved2[6];
|
||||
uint16_t bcdUSB488;
|
||||
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t supportsTrigger :1;
|
||||
uint8_t supportsREN_GTL_LLO :1;
|
||||
uint8_t is488_2 :1;
|
||||
} bmIntfcCapabilities488;
|
||||
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t DT1 :1;
|
||||
uint8_t RL1 :1;
|
||||
uint8_t SR1 :1;
|
||||
uint8_t SCPI :1;
|
||||
} bmDevCapabilities488;
|
||||
uint8_t _reserved3[8];
|
||||
} usbtmc_response_capabilities_488_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_488_t) == 0x18, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t USBTMC_status;
|
||||
uint8_t bTag;
|
||||
uint8_t statusByte;
|
||||
} usbtmc_read_stb_rsp_488_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bNotify1; // Must be USB488_bNOTIFY1_SRQ
|
||||
uint8_t StatusByte;
|
||||
} usbtmc_srq_interrupt_488_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_srq_interrupt_488_t) == 2u, "struct wrong length");
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
unsigned int bTag : 7;
|
||||
unsigned int one : 1;
|
||||
} bNotify1;
|
||||
uint8_t StatusByte;
|
||||
} usbtmc_read_stb_interrupt_488_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_interrupt_488_t) == 2u, "struct wrong length");
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Nathan Conrad
|
||||
*
|
||||
* 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 library is not fully reentrant, though it is reentrant from the view
|
||||
* of either the application layer or the USB stack. Due to its locking,
|
||||
* it is not safe to call its functions from interrupts.
|
||||
*
|
||||
* The one exception is that its functions may not be called from the application
|
||||
* until the USB stack is initialized. This should not be a problem since the
|
||||
* device shouldn't be sending messages until it receives a request from the
|
||||
* host.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* In the case of single-CPU "no OS", this task is never preempted other than by
|
||||
* interrupts, and the USBTMC code isn't called by interrupts, so all is OK. For "no OS",
|
||||
* the mutex structure's main effect is to disable the USB interrupts.
|
||||
* With an OS, this class driver uses the OSAL to perform locking. The code uses a single lock
|
||||
* and does not call outside of this class with a lock held, so deadlocks won't happen.
|
||||
*/
|
||||
|
||||
//Limitations:
|
||||
// "vendor-specific" commands are handled similar to normal messages, except that the MsgID is changed to "vendor-specific".
|
||||
// Dealing with "termchar" must be handled by the application layer,
|
||||
// though additional error checking is does in this module.
|
||||
// talkOnly and listenOnly are NOT supported. They're not permitted
|
||||
// in USB488, anyway.
|
||||
|
||||
/* Supported:
|
||||
*
|
||||
* Notification pulse
|
||||
* Trigger
|
||||
* Read status byte (both by interrupt endpoint and control message)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// TODO:
|
||||
// USBTMC 3.2.2 error conditions not strictly followed
|
||||
// No local lock-out, REN, or GTL.
|
||||
// Clear message available status byte at the correct time? (488 4.3.1.3)
|
||||
// Ability to defer status byte transmission
|
||||
// Transmission of status byte in response to USB488 SRQ condition
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_USBTMC)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "usbtmc_device.h"
|
||||
|
||||
// Buffer size must be an exact multiple of the max packet size for both
|
||||
// bulk (up to 64 bytes for FS, 512 bytes for HS). In addation, this driver
|
||||
// imposes a minimum buffer size of 32 bytes.
|
||||
#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
|
||||
#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
|
||||
#define CFG_TUD_USBTMC_INT_EP_SIZE 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The state machine does not allow simultaneous reading and writing. This is
|
||||
* consistent with USBTMC.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
STATE_CLOSED,// Endpoints have not yet been opened since USB reset
|
||||
STATE_NAK, // Bulk-out endpoint is in NAK state.
|
||||
STATE_IDLE, // Bulk-out endpoint is waiting for CMD.
|
||||
STATE_RCV, // Bulk-out is receiving DEV_DEP message
|
||||
STATE_TX_REQUESTED,
|
||||
STATE_TX_INITIATED,
|
||||
STATE_TX_SHORTED,
|
||||
STATE_CLEARING,
|
||||
STATE_ABORTING_BULK_IN,
|
||||
STATE_ABORTING_BULK_IN_SHORTED,// aborting, and short packet has been queued for transmission
|
||||
STATE_ABORTING_BULK_IN_ABORTED,// aborting, and short packet has been transmitted
|
||||
STATE_ABORTING_BULK_OUT,
|
||||
STATE_NUM_STATES
|
||||
} usbtmcd_state_enum;
|
||||
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
typedef usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t;
|
||||
#else
|
||||
typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile usbtmcd_state_enum state;
|
||||
|
||||
uint8_t itf_id;
|
||||
uint8_t rhport;
|
||||
uint8_t ep_bulk_in;
|
||||
uint8_t ep_bulk_out;
|
||||
uint8_t ep_int_in;
|
||||
uint32_t ep_bulk_in_wMaxPacketSize;
|
||||
uint32_t ep_bulk_out_wMaxPacketSize;
|
||||
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)
|
||||
|
||||
uint8_t lastBulkOutTag;// used for aborts (mostly)
|
||||
uint8_t lastBulkInTag; // used for aborts (mostly)
|
||||
|
||||
uint8_t const *devInBuffer;// pointer to application-layer used for transmissions
|
||||
|
||||
usbtmc_capabilities_specific_t const *capabilities;
|
||||
} usbtmc_interface_state_t;
|
||||
|
||||
typedef struct {
|
||||
// IN buffer is only used for first packet, not the remainder in order to deal with prepending header
|
||||
TUD_EPBUF_DEF(epin, USBTMCD_BUFFER_SIZE);
|
||||
|
||||
// OUT buffer receives one packet at a time
|
||||
TUD_EPBUF_DEF(epout, USBTMCD_BUFFER_SIZE);
|
||||
|
||||
// Buffer int msg
|
||||
TUD_EPBUF_DEF(epnotif, CFG_TUD_USBTMC_INT_EP_SIZE);
|
||||
} usbtmc_epbuf_t;
|
||||
|
||||
static usbtmc_interface_state_t usbtmc_state = {
|
||||
.itf_id = 0xFF,
|
||||
};
|
||||
|
||||
CFG_TUD_MEM_SECTION static usbtmc_epbuf_t usbtmc_epbuf;
|
||||
|
||||
// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers
|
||||
TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u, "USBTMC dev buffer size too small");
|
||||
|
||||
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len);
|
||||
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen);
|
||||
|
||||
|
||||
// USBTMC Device Callbacks weak implementations
|
||||
TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult) {
|
||||
(void) msg;
|
||||
(void) tmcResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg) {
|
||||
(void) msg;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
tu_static uint8_t termChar;
|
||||
#endif
|
||||
|
||||
tu_static uint8_t termCharRequested = false;
|
||||
|
||||
tu_static bool usbtmcVendorSpecificRequested = false;
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
static OSAL_MUTEX_DEF(usbtmcLockBuffer);
|
||||
#endif
|
||||
osal_mutex_t usbtmcLock;
|
||||
|
||||
// Our own private lock, mostly for the state variable.
|
||||
#define criticalEnter() \
|
||||
do { (void) osal_mutex_lock(usbtmcLock, OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
|
||||
#define criticalLeave() \
|
||||
do { (void) osal_mutex_unlock(usbtmcLock); } while (0)
|
||||
|
||||
static bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState) {
|
||||
bool ret = true;
|
||||
criticalEnter();
|
||||
usbtmcd_state_enum oldState = usbtmc_state.state;
|
||||
if (oldState == expectedState) {
|
||||
usbtmc_state.state = newState;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
criticalLeave();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// called from app
|
||||
// We keep a reference to the buffer, so it MUST not change until the app is
|
||||
// notified that the transfer is complete.
|
||||
// length of data is specified in the hdr.
|
||||
|
||||
// We can't just send the whole thing at once because we need to concatanate the
|
||||
// header with the data.
|
||||
bool tud_usbtmc_transmit_dev_msg_data(
|
||||
const void *data, size_t len,
|
||||
bool endOfMessage,
|
||||
bool usingTermChar) {
|
||||
const unsigned int txBufLen = USBTMCD_BUFFER_SIZE;
|
||||
|
||||
#ifndef NDEBUG
|
||||
TU_ASSERT(len > 0u);
|
||||
TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
|
||||
TU_ASSERT(usbtmc_state.transfer_size_sent == 0u);
|
||||
if (usingTermChar) {
|
||||
TU_ASSERT(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
|
||||
TU_ASSERT(termCharRequested);
|
||||
TU_ASSERT(((uint8_t const *) data)[len - 1u] == termChar);
|
||||
}
|
||||
#endif
|
||||
|
||||
TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
|
||||
usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t *) usbtmc_epbuf.epin;
|
||||
tu_varclr(hdr);
|
||||
if (usbtmcVendorSpecificRequested) {
|
||||
hdr->header.MsgID = USBTMC_MSGID_VENDOR_SPECIFIC_IN;
|
||||
} else {
|
||||
hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
|
||||
}
|
||||
hdr->header.bTag = usbtmc_state.lastBulkInTag;
|
||||
hdr->header.bTagInverse = (uint8_t) ~(usbtmc_state.lastBulkInTag);
|
||||
hdr->TransferSize = len;
|
||||
hdr->bmTransferAttributes.EOM = endOfMessage;
|
||||
hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
|
||||
|
||||
// Copy in the header
|
||||
const size_t headerLen = sizeof(*hdr);
|
||||
const size_t dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ? len : (txBufLen - headerLen);
|
||||
const size_t packetLen = headerLen + dataLen;
|
||||
|
||||
memcpy((uint8_t *) (usbtmc_epbuf.epin) + headerLen, data, dataLen);
|
||||
usbtmc_state.transfer_size_remaining = len - dataLen;
|
||||
usbtmc_state.transfer_size_sent = dataLen;
|
||||
usbtmc_state.devInBuffer = (uint8_t const *) data + (dataLen);
|
||||
|
||||
bool stateChanged =
|
||||
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
|
||||
TU_VERIFY(stateChanged);
|
||||
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_usbtmc_transmit_notification_data(const void *data, size_t len) {
|
||||
#ifndef NDEBUG
|
||||
TU_ASSERT(len > 0);
|
||||
TU_ASSERT(usbtmc_state.ep_int_in != 0);
|
||||
#endif
|
||||
TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
|
||||
|
||||
TU_VERIFY(tu_memcpy_s(usbtmc_epbuf.epnotif, CFG_TUD_USBTMC_INT_EP_SIZE, data, len) == 0);
|
||||
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t) len));
|
||||
return true;
|
||||
}
|
||||
|
||||
void usbtmcd_init_cb(void) {
|
||||
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
|
||||
#ifndef NDEBUG
|
||||
#if CFG_TUD_USBTMC_ENABLE_488
|
||||
// Per USB488 spec: table 8
|
||||
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly, );
|
||||
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly, );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
|
||||
}
|
||||
|
||||
bool usbtmcd_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_delete(usbtmcLock);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
|
||||
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
|
||||
// Only 2 or 3 endpoints are allowed for USBTMC.
|
||||
TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints == 3), 0);
|
||||
#endif
|
||||
|
||||
TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
|
||||
|
||||
// Interface
|
||||
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 && 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:
|
||||
// Ensure buffer is an exact multiple of the maxPacketSize
|
||||
TU_ASSERT((USBTMCD_BUFFER_SIZE % tu_edpt_packet_size(ep_desc)) == 0, 0);
|
||||
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress;
|
||||
usbtmc_state.ep_bulk_in_wMaxPacketSize = tu_edpt_packet_size(ep_desc);
|
||||
} else {
|
||||
usbtmc_state.ep_bulk_out = ep_desc->bEndpointAddress;
|
||||
usbtmc_state.ep_bulk_out_wMaxPacketSize = tu_edpt_packet_size(ep_desc);
|
||||
}
|
||||
|
||||
break;
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
#ifndef NDEBUG
|
||||
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, 0);
|
||||
}
|
||||
TU_ASSERT(usbd_edpt_open(rhport, ep_desc), 0);
|
||||
found_endpoints++;
|
||||
}
|
||||
|
||||
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, 0);
|
||||
TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
|
||||
if (itf_desc->bNumEndpoints == 2) {
|
||||
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
|
||||
} else if (itf_desc->bNumEndpoints == 3) {
|
||||
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, 0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
atomicChangeState(STATE_CLOSED, STATE_NAK);
|
||||
tud_usbtmc_open_cb(itf_desc->iInterface);
|
||||
|
||||
return drv_len;
|
||||
}
|
||||
// Tell USBTMC class to set its bulk-in EP to ACK so that it can
|
||||
// receive USBTMC commands.
|
||||
// Returns false if it was already in an ACK state or is busy
|
||||
// processing a command (such as a clear). Returns true if it was
|
||||
// in the NAK state and successfully transitioned to the ACK wait
|
||||
// state.
|
||||
bool tud_usbtmc_start_bus_read(void) {
|
||||
usbtmcd_state_enum oldState = usbtmc_state.state;
|
||||
switch (oldState) {
|
||||
// These may transition to IDLE
|
||||
case STATE_NAK:
|
||||
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||
TU_VERIFY(atomicChangeState(oldState, STATE_IDLE));
|
||||
break;
|
||||
// When receiving, let it remain receiving
|
||||
case STATE_RCV:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_epbuf.epout, (uint16_t) usbtmc_state.ep_bulk_out_wMaxPacketSize));
|
||||
return true;
|
||||
}
|
||||
|
||||
void usbtmcd_reset_cb(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usbtmc_capabilities_specific_t const *capabilities = tud_usbtmc_get_capabilities_cb();
|
||||
|
||||
criticalEnter();
|
||||
tu_varclr(&usbtmc_state);
|
||||
usbtmc_state.capabilities = capabilities;
|
||||
usbtmc_state.itf_id = 0xFFu;
|
||||
criticalLeave();
|
||||
}
|
||||
|
||||
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len) {
|
||||
(void) rhport;
|
||||
// return true upon failure, as we can assume error is being handled elsewhere.
|
||||
TU_VERIFY(atomicChangeState(STATE_IDLE, STATE_RCV), true);
|
||||
usbtmc_state.transfer_size_sent = 0u;
|
||||
|
||||
// must be a header, should have been confirmed before calling here.
|
||||
usbtmc_msg_request_dev_dep_out *msg = (usbtmc_msg_request_dev_dep_out *) data;
|
||||
usbtmc_state.transfer_size_remaining = msg->TransferSize;
|
||||
TU_VERIFY(tud_usbtmc_msgBulkOut_start_cb(msg));
|
||||
|
||||
TU_VERIFY(handle_devMsgOut(rhport, (uint8_t *) data + sizeof(*msg), len - sizeof(*msg), len));
|
||||
usbtmc_state.lastBulkOutTag = msg->header.bTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen) {
|
||||
(void) rhport;
|
||||
// return true upon failure, as we can assume error is being handled elsewhere.
|
||||
TU_VERIFY(usbtmc_state.state == STATE_RCV, true);
|
||||
|
||||
bool shortPacket = (packetLen < usbtmc_state.ep_bulk_out_wMaxPacketSize);
|
||||
|
||||
// Packet is to be considered complete when we get enough data or at a short packet.
|
||||
bool atEnd = false;
|
||||
if (len >= usbtmc_state.transfer_size_remaining || shortPacket) {
|
||||
atEnd = true;
|
||||
TU_VERIFY(atomicChangeState(STATE_RCV, STATE_NAK));
|
||||
}
|
||||
|
||||
len = tu_min32(len, usbtmc_state.transfer_size_remaining);
|
||||
|
||||
usbtmc_state.transfer_size_remaining -= len;
|
||||
usbtmc_state.transfer_size_sent += len;
|
||||
|
||||
// App may (should?) call the wait_for_bus() command at this point
|
||||
if (!tud_usbtmc_msg_data_cb(data, len, atEnd)) {
|
||||
// TODO: Go to an error state upon failure other than just stalling the EP?
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_devMsgIn(void *data, size_t len) {
|
||||
TU_VERIFY(len == sizeof(usbtmc_msg_request_dev_dep_in));
|
||||
usbtmc_msg_request_dev_dep_in *msg = (usbtmc_msg_request_dev_dep_in *) data;
|
||||
bool stateChanged = atomicChangeState(STATE_IDLE, STATE_TX_REQUESTED);
|
||||
TU_VERIFY(stateChanged);
|
||||
usbtmc_state.lastBulkInTag = msg->header.bTag;
|
||||
usbtmc_state.transfer_size_remaining = msg->TransferSize;
|
||||
usbtmc_state.transfer_size_sent = 0u;
|
||||
|
||||
termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
|
||||
|
||||
#ifndef NDEBUG
|
||||
termChar = msg->TermChar;
|
||||
#endif
|
||||
|
||||
if (termCharRequested)
|
||||
TU_VERIFY(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
|
||||
|
||||
TU_VERIFY(tud_usbtmc_msgBulkIn_request_cb(msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
TU_VERIFY(result == XFER_RESULT_SUCCESS);
|
||||
//uart_tx_str_sync("TMC XFER CB\r\n");
|
||||
if (usbtmc_state.state == STATE_CLEARING) {
|
||||
return true; /* I think we can ignore everything here */
|
||||
}
|
||||
|
||||
if (ep_addr == usbtmc_state.ep_bulk_out) {
|
||||
usbtmc_msg_generic_t *msg = NULL;
|
||||
|
||||
switch (usbtmc_state.state) {
|
||||
case STATE_IDLE: {
|
||||
TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
|
||||
msg = (usbtmc_msg_generic_t *) (usbtmc_epbuf.epout);
|
||||
uint8_t invInvTag = (uint8_t) ~(msg->header.bTagInverse);
|
||||
TU_VERIFY(msg->header.bTag == invInvTag);
|
||||
TU_VERIFY(msg->header.bTag != 0x00);
|
||||
|
||||
switch (msg->header.MsgID) {
|
||||
case USBTMC_MSGID_DEV_DEP_MSG_OUT:
|
||||
usbtmcVendorSpecificRequested = false;
|
||||
if (!handle_devMsgOutStart(rhport, msg, xferred_bytes)) {
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case USBTMC_MSGID_DEV_DEP_MSG_IN:
|
||||
usbtmcVendorSpecificRequested = false;
|
||||
TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
|
||||
break;
|
||||
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
case USBTMC_MSGID_USB488_TRIGGER:
|
||||
// Spec says we halt the EP if we didn't declare we support it.
|
||||
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
|
||||
TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
|
||||
|
||||
break;
|
||||
#endif
|
||||
case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
|
||||
usbtmcVendorSpecificRequested = true;
|
||||
if (!handle_devMsgOutStart(rhport, msg, xferred_bytes)) {
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
|
||||
usbtmcVendorSpecificRequested = true;
|
||||
TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
|
||||
break;
|
||||
|
||||
default:
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case STATE_RCV:
|
||||
if (!handle_devMsgOut(rhport, usbtmc_epbuf.epout, xferred_bytes, xferred_bytes)) {
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case STATE_ABORTING_BULK_OUT:
|
||||
// Should be stalled by now, shouldn't have received a packet.
|
||||
return false;
|
||||
|
||||
case STATE_TX_REQUESTED:
|
||||
case STATE_TX_INITIATED:
|
||||
case STATE_ABORTING_BULK_IN:
|
||||
case STATE_ABORTING_BULK_IN_SHORTED:
|
||||
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (ep_addr == usbtmc_state.ep_bulk_in) {
|
||||
switch (usbtmc_state.state) {
|
||||
case STATE_TX_SHORTED:
|
||||
TU_VERIFY(atomicChangeState(STATE_TX_SHORTED, STATE_NAK));
|
||||
TU_VERIFY(tud_usbtmc_msgBulkIn_complete_cb());
|
||||
break;
|
||||
|
||||
case STATE_TX_INITIATED:
|
||||
if (usbtmc_state.transfer_size_remaining >= USBTMCD_BUFFER_SIZE) {
|
||||
// Copy buffer to ensure alignment correctness
|
||||
memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, USBTMCD_BUFFER_SIZE);
|
||||
TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, USBTMCD_BUFFER_SIZE));
|
||||
usbtmc_state.devInBuffer += USBTMCD_BUFFER_SIZE;
|
||||
usbtmc_state.transfer_size_remaining -= USBTMCD_BUFFER_SIZE;
|
||||
usbtmc_state.transfer_size_sent += USBTMCD_BUFFER_SIZE;
|
||||
} else// last packet
|
||||
{
|
||||
size_t packetLen = usbtmc_state.transfer_size_remaining;
|
||||
memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
|
||||
usbtmc_state.transfer_size_sent += packetLen;
|
||||
usbtmc_state.transfer_size_remaining = 0;
|
||||
usbtmc_state.devInBuffer = NULL;
|
||||
TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) packetLen));
|
||||
if (((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0)) {
|
||||
usbtmc_state.state = STATE_TX_SHORTED;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case STATE_ABORTING_BULK_IN:
|
||||
// need to send short packet (ZLP?)
|
||||
TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u));
|
||||
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
|
||||
return true;
|
||||
|
||||
case STATE_ABORTING_BULK_IN_SHORTED:
|
||||
/* Done. :)*/
|
||||
usbtmc_state.state = STATE_ABORTING_BULK_IN_ABORTED;
|
||||
return true;
|
||||
|
||||
default:
|
||||
TU_ASSERT(false);
|
||||
}
|
||||
} else if (ep_addr == usbtmc_state.ep_int_in) {
|
||||
TU_VERIFY(tud_usbtmc_notification_complete_cb());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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)
|
||||
uint8_t bTag;
|
||||
#endif
|
||||
|
||||
if ((request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) &&
|
||||
(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT) &&
|
||||
(request->bRequest == TUSB_REQ_CLEAR_FEATURE) &&
|
||||
(request->wValue == TUSB_REQ_FEATURE_EDPT_HALT)) {
|
||||
uint32_t ep_addr = (request->wIndex);
|
||||
|
||||
// At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT,
|
||||
// the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if
|
||||
// the EP is not halted. The only USBD API interface to do this is to stall and then un-stall the EP.
|
||||
if (ep_addr == usbtmc_state.ep_bulk_out) {
|
||||
criticalEnter();
|
||||
usbd_edpt_stall(rhport, (uint8_t) ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, (uint8_t) ep_addr);
|
||||
usbtmc_state.state = STATE_NAK;// USBD core has placed EP in NAK state for us
|
||||
criticalLeave();
|
||||
tud_usbtmc_bulkOut_clearFeature_cb();
|
||||
} else if (ep_addr == usbtmc_state.ep_bulk_in) {
|
||||
usbd_edpt_stall(rhport, (uint8_t) ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, (uint8_t) ep_addr);
|
||||
tud_usbtmc_bulkIn_clearFeature_cb();
|
||||
} else if ((usbtmc_state.ep_int_in != 0) && (ep_addr == usbtmc_state.ep_int_in)) {
|
||||
// Clearing interrupt in EP
|
||||
usbd_edpt_stall(rhport, (uint8_t) ep_addr);
|
||||
usbd_edpt_clear_stall(rhport, (uint8_t) ep_addr);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we only handle class requests.
|
||||
if (request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verification that we own the interface is unneeded since it's been routed to us specifically.
|
||||
|
||||
switch (request->bRequest) {
|
||||
// USBTMC required requests
|
||||
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT: {
|
||||
usbtmc_initiate_abort_rsp_t rsp = {
|
||||
.bTag = usbtmc_state.lastBulkOutTag,
|
||||
};
|
||||
TU_VERIFY(request->bmRequestType == 0xA2);// in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
|
||||
|
||||
// wValue is the requested bTag to abort
|
||||
if (usbtmc_state.state != STATE_RCV) {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
|
||||
} else if (usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu)) {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
|
||||
} else {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||
// Check if we've queued a short packet
|
||||
criticalEnter();
|
||||
usbtmc_state.state = STATE_ABORTING_BULK_OUT;
|
||||
criticalLeave();
|
||||
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_out_cb(&(rsp.USBTMC_status)));
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
}
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS: {
|
||||
usbtmc_check_abort_bulk_rsp_t rsp = {
|
||||
.USBTMC_status = USBTMC_STATUS_SUCCESS,
|
||||
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent};
|
||||
TU_VERIFY(request->bmRequestType == 0xA2);// in,class,EP
|
||||
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
|
||||
TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN: {
|
||||
usbtmc_initiate_abort_rsp_t rsp = {
|
||||
.bTag = usbtmc_state.lastBulkInTag,
|
||||
};
|
||||
TU_VERIFY(request->bmRequestType == 0xA2);// in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_in);
|
||||
// wValue is the requested bTag to abort
|
||||
if ((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED) &&
|
||||
usbtmc_state.lastBulkInTag == (request->wValue & 0x7Fu)) {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||
usbtmc_state.transfer_size_remaining = 0u;
|
||||
// Check if we've queued a short packet
|
||||
criticalEnter();
|
||||
usbtmc_state.state = ((usbtmc_state.transfer_size_sent % usbtmc_state.ep_bulk_in_wMaxPacketSize) == 0) ? STATE_ABORTING_BULK_IN : STATE_ABORTING_BULK_IN_SHORTED;
|
||||
criticalLeave();
|
||||
if (usbtmc_state.transfer_size_sent == 0) {
|
||||
// Send short packet, nothing is in the buffer yet
|
||||
TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t) 0u));
|
||||
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
|
||||
}
|
||||
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
|
||||
} else if ((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED)) {// FIXME: Unsure how to check if the OUT endpoint fifo is non-empty....
|
||||
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
|
||||
} else {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
|
||||
}
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS: {
|
||||
TU_VERIFY(request->bmRequestType == 0xA2);// in,class,EP
|
||||
TU_VERIFY(request->wLength == 8u);
|
||||
|
||||
usbtmc_check_abort_bulk_rsp_t rsp =
|
||||
{
|
||||
.USBTMC_status = USBTMC_STATUS_FAILED,
|
||||
.bmAbortBulkIn =
|
||||
{
|
||||
.BulkInFifoBytes = (usbtmc_state.state != STATE_ABORTING_BULK_IN_ABORTED)},
|
||||
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent,
|
||||
};
|
||||
TU_VERIFY(tud_usbtmc_check_abort_bulk_in_cb(&rsp));
|
||||
criticalEnter();
|
||||
switch (usbtmc_state.state) {
|
||||
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||
usbtmc_state.state = STATE_IDLE;
|
||||
break;
|
||||
case STATE_ABORTING_BULK_IN:
|
||||
case STATE_ABORTING_BULK_OUT:
|
||||
rsp.USBTMC_status = USBTMC_STATUS_PENDING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
criticalLeave();
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_INITIATE_CLEAR: {
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);// in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
|
||||
// After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
|
||||
// control endpoint response shown in Table 31, and clear all input buffers and output buffers.
|
||||
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||
usbtmc_state.transfer_size_remaining = 0;
|
||||
criticalEnter();
|
||||
usbtmc_state.state = STATE_CLEARING;
|
||||
criticalLeave();
|
||||
TU_VERIFY(tud_usbtmc_initiate_clear_cb(&tmcStatusCode));
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &tmcStatusCode, sizeof(tmcStatusCode)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_CHECK_CLEAR_STATUS: {
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);// in,class,interface
|
||||
usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
|
||||
TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
|
||||
|
||||
if (usbd_edpt_busy(rhport, usbtmc_state.ep_bulk_in)) {
|
||||
// Stuff stuck in TX buffer?
|
||||
clearStatusRsp.bmClear.BulkInFifoBytes = 1;
|
||||
clearStatusRsp.USBTMC_status = USBTMC_STATUS_PENDING;
|
||||
} else {
|
||||
// Let app check if it's clear
|
||||
TU_VERIFY(tud_usbtmc_check_clear_cb(&clearStatusRsp));
|
||||
}
|
||||
if (clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS) {
|
||||
criticalEnter();
|
||||
usbtmc_state.state = STATE_IDLE;
|
||||
criticalLeave();
|
||||
}
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &clearStatusRsp, sizeof(clearStatusRsp)));
|
||||
return true;
|
||||
}
|
||||
|
||||
case USBTMC_bREQUEST_GET_CAPABILITIES: {
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);// in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(*(usbtmc_state.capabilities)));
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) (uintptr_t) usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
|
||||
return true;
|
||||
}
|
||||
// USBTMC Optional Requests
|
||||
|
||||
case USBTMC_bREQUEST_INDICATOR_PULSE:// Optional
|
||||
{
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);// in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
|
||||
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse);
|
||||
TU_VERIFY(tud_usbtmc_indicator_pulse_cb(request, &tmcStatusCode));
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &tmcStatusCode, sizeof(tmcStatusCode)));
|
||||
return true;
|
||||
}
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
|
||||
// USB488 required requests
|
||||
case USB488_bREQUEST_READ_STATUS_BYTE: {
|
||||
usbtmc_read_stb_rsp_488_t rsp;
|
||||
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||
TU_VERIFY(request->wLength == sizeof(rsp));// in,class,interface
|
||||
|
||||
bTag = request->wValue & 0x7F;
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);
|
||||
TU_VERIFY((request->wValue & (~0x7F)) == 0u);// Other bits are required to be zero (USB488v1.0 Table 11)
|
||||
TU_VERIFY(bTag >= 0x02 && bTag <= 127);
|
||||
TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
|
||||
TU_VERIFY(request->wLength == 0x0003);
|
||||
rsp.bTag = (uint8_t) bTag;
|
||||
if (usbtmc_state.ep_int_in != 0) {
|
||||
rsp.statusByte = 0x00;// Use interrupt endpoint, instead. Must be 0x00 (USB488v1.0 4.3.1.2)
|
||||
if (usbd_edpt_busy(rhport, usbtmc_state.ep_int_in)) {
|
||||
rsp.USBTMC_status = USB488_STATUS_INTERRUPT_IN_BUSY;
|
||||
} else {
|
||||
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||
usbtmc_read_stb_interrupt_488_t intMsg =
|
||||
{
|
||||
.bNotify1 = {
|
||||
.one = 1,
|
||||
.bTag = bTag & 0x7Fu,
|
||||
},
|
||||
.StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))};
|
||||
// Must be queued before control request response sent (USB488v1.0 4.3.1.2)
|
||||
usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void *) &intMsg, sizeof(intMsg));
|
||||
}
|
||||
} else {
|
||||
rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
|
||||
}
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
|
||||
return true;
|
||||
}
|
||||
// USB488 optional requests
|
||||
case USB488_bREQUEST_REN_CONTROL:
|
||||
case USB488_bREQUEST_GO_TO_LOCAL:
|
||||
case USB488_bREQUEST_LOCAL_LOCKOUT: {
|
||||
TU_VERIFY(request->bmRequestType == 0xA1);// in,class,interface
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CFG_TUD_TSMC */
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 N Conrad
|
||||
*
|
||||
* 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 CLASS_USBTMC_USBTMC_DEVICE_H_
|
||||
#define CLASS_USBTMC_USBTMC_DEVICE_H_
|
||||
|
||||
#include "usbtmc.h"
|
||||
|
||||
// Enable 488 mode by default
|
||||
#if !defined(CFG_TUD_USBTMC_ENABLE_488)
|
||||
#define CFG_TUD_USBTMC_ENABLE_488 (1)
|
||||
#endif
|
||||
|
||||
/***********************************************
|
||||
* Functions to be implemented by the class implementation
|
||||
*/
|
||||
|
||||
// In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after:
|
||||
// * tud_usbtmc_open_cb
|
||||
// * tud_usbtmc_msg_data_cb
|
||||
// * tud_usbtmc_msgBulkIn_complete_cb
|
||||
// * tud_usbtmc_msg_trigger_cb
|
||||
// * (successful) tud_usbtmc_check_abort_bulk_out_cb
|
||||
// * (successful) tud_usbtmc_check_abort_bulk_in_cb
|
||||
// * (successful) tud_usmtmc_bulkOut_clearFeature_cb
|
||||
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
usbtmc_response_capabilities_488_t const * tud_usbtmc_get_capabilities_cb(void);
|
||||
#else
|
||||
usbtmc_response_capabilities_t const * tud_usbtmc_get_capabilities_cb(void);
|
||||
#endif
|
||||
|
||||
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_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);
|
||||
void tud_usbtmc_bulkIn_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
|
||||
|
||||
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult);
|
||||
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult);
|
||||
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult);
|
||||
|
||||
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
|
||||
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
|
||||
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
|
||||
|
||||
// The interrupt-IN endpoint buffer was transmitted to the host. Use
|
||||
// tud_usbtmc_transmit_notification_data to send another notification.
|
||||
bool tud_usbtmc_notification_complete_cb(void);
|
||||
|
||||
// Indicator pulse should be 0.5 to 1.0 seconds long
|
||||
bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
|
||||
|
||||
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult);
|
||||
bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
|
||||
#endif
|
||||
|
||||
// Called from app
|
||||
//
|
||||
// We keep a reference to the buffer, so it MUST not change until the app is
|
||||
// notified that the transfer is complete.
|
||||
bool tud_usbtmc_transmit_dev_msg_data(
|
||||
const void * data, size_t len,
|
||||
bool endOfMessage, bool usingTermChar);
|
||||
|
||||
// Buffers a notification to be sent to the host. The data starts
|
||||
// with the bNotify1 field, see the USBTMC Specification, Table 13.
|
||||
//
|
||||
// If the previous notification data has not yet been sent, this
|
||||
// returns false.
|
||||
//
|
||||
// Requires an interrupt endpoint in the interface.
|
||||
bool tud_usbtmc_transmit_notification_data(const void * data, size_t len);
|
||||
|
||||
bool tud_usbtmc_start_bus_read(void);
|
||||
|
||||
|
||||
/* "callbacks" from USB device core */
|
||||
|
||||
void usbtmcd_init_cb(void);
|
||||
bool usbtmcd_deinit(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);
|
||||
|
||||
#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
|
||||
295
managed_components/espressif__tinyusb/src/class/vendor/vendor_device.c
vendored
Normal file
295
managed_components/espressif__tinyusb/src/class/vendor/vendor_device.c
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_VENDOR)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "vendor_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
struct {
|
||||
tu_edpt_stream_t stream;
|
||||
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
|
||||
uint8_t ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
#endif
|
||||
} tx;
|
||||
|
||||
struct {
|
||||
tu_edpt_stream_t stream;
|
||||
#if CFG_TUD_VENDOR_RX_BUFSIZE > 0
|
||||
uint8_t ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||
#endif
|
||||
} rx;
|
||||
|
||||
} vendord_interface_t;
|
||||
|
||||
#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num))
|
||||
|
||||
static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
|
||||
|
||||
typedef struct {
|
||||
TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE);
|
||||
TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE);
|
||||
} vendord_epbuf_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Weak stubs: invoked if no strong implementation is available
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) {
|
||||
(void) itf;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
}
|
||||
|
||||
TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) {
|
||||
(void) itf;
|
||||
(void) sent_bytes;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Application API
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
bool tud_vendor_n_mounted(uint8_t itf) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
return p_itf->rx.stream.ep_addr || p_itf->tx.stream.ep_addr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Read API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_vendor_n_available(uint8_t itf) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
|
||||
return tu_edpt_stream_read_available(&p_itf->rx.stream);
|
||||
}
|
||||
|
||||
bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
|
||||
return tu_edpt_stream_peek(&p_itf->rx.stream, u8);
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
return tu_edpt_stream_read(rhport, &p_itf->rx.stream, buffer, bufsize);
|
||||
}
|
||||
|
||||
void tud_vendor_n_read_flush (uint8_t itf) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, );
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
tu_edpt_stream_clear(&p_itf->rx.stream);
|
||||
tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Write API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
return tu_edpt_stream_write(rhport, &p_itf->tx.stream, buffer, (uint16_t) bufsize);
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_write_flush (uint8_t itf) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
return tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream);
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_write_available (uint8_t itf) {
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
const uint8_t rhport = 0;
|
||||
|
||||
return tu_edpt_stream_write_available(rhport, &p_itf->tx.stream);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void) {
|
||||
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i];
|
||||
|
||||
uint8_t* rx_ff_buf =
|
||||
#if CFG_TUD_VENDOR_RX_BUFSIZE > 0
|
||||
p_itf->rx.ff_buf;
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
tu_edpt_stream_init(&p_itf->rx.stream, false, false, false,
|
||||
rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE,
|
||||
p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE);
|
||||
|
||||
uint8_t* tx_ff_buf =
|
||||
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
|
||||
p_itf->tx.ff_buf;
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
tu_edpt_stream_init(&p_itf->tx.stream, false, true, false,
|
||||
tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE,
|
||||
p_epbuf->epin, CFG_TUD_VENDOR_EPSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
bool vendord_deinit(void) {
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
tu_edpt_stream_deinit(&p_itf->rx.stream);
|
||||
tu_edpt_stream_deinit(&p_itf->tx.stream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void vendord_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
|
||||
tu_edpt_stream_clear(&p_itf->rx.stream);
|
||||
tu_edpt_stream_clear(&p_itf->tx.stream);
|
||||
tu_edpt_stream_close(&p_itf->rx.stream);
|
||||
tu_edpt_stream_close(&p_itf->tx.stream);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
|
||||
const uint8_t* p_desc = tu_desc_next(desc_itf);
|
||||
|
||||
// Find available interface
|
||||
vendord_interface_t* p_vendor = NULL;
|
||||
uint8_t itf;
|
||||
for(itf=0; itf<CFG_TUD_VENDOR; itf++) {
|
||||
if (!tud_vendor_n_mounted(itf)) {
|
||||
p_vendor = &_vendord_itf[itf];
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_VERIFY(p_vendor, 0);
|
||||
|
||||
p_vendor->itf_num = desc_itf->bInterfaceNumber;
|
||||
while (tu_desc_in_bounds(p_desc, desc_end)) {
|
||||
const uint8_t desc_type = tu_desc_type(p_desc);
|
||||
if (desc_type == TUSB_DESC_INTERFACE || desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) {
|
||||
break; // end of this interface
|
||||
} else if (desc_type == TUSB_DESC_ENDPOINT) {
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
|
||||
|
||||
// open endpoint stream, skip if already opened
|
||||
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
if (p_vendor->tx.stream.ep_addr == 0) {
|
||||
tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
|
||||
tud_vendor_n_write_flush(itf);
|
||||
}
|
||||
} else {
|
||||
if (p_vendor->rx.stream.ep_addr == 0) {
|
||||
tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
|
||||
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf);
|
||||
}
|
||||
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
uint8_t itf;
|
||||
vendord_interface_t* p_vendor;
|
||||
|
||||
for (itf = 0; itf < CFG_TUD_VENDOR; itf++) {
|
||||
p_vendor = &_vendord_itf[itf];
|
||||
if ((ep_addr == p_vendor->rx.stream.ep_addr) || (ep_addr == p_vendor->tx.stream.ep_addr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_VERIFY(itf < CFG_TUD_VENDOR);
|
||||
vendord_epbuf_t* p_epbuf = &_vendord_epbuf[itf];
|
||||
|
||||
if ( ep_addr == p_vendor->rx.stream.ep_addr ) {
|
||||
// Received new data: put into stream's fifo
|
||||
tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes);
|
||||
|
||||
// Invoked callback if any
|
||||
tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes);
|
||||
|
||||
tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream);
|
||||
} else if ( ep_addr == p_vendor->tx.stream.ep_addr ) {
|
||||
// Send complete
|
||||
tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
|
||||
|
||||
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
|
||||
// try to send more if possible
|
||||
if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->tx.stream) ) {
|
||||
// If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero
|
||||
tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->tx.stream, xferred_bytes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
144
managed_components/espressif__tinyusb/src/class/vendor/vendor_device.h
vendored
Normal file
144
managed_components/espressif__tinyusb/src/class/vendor/vendor_device.h
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_VENDOR_DEVICE_H_
|
||||
#define _TUSB_VENDOR_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EPSIZE
|
||||
#define CFG_TUD_VENDOR_EPSIZE 64
|
||||
#endif
|
||||
|
||||
// RX FIFO can be disabled by setting this value to 0
|
||||
#ifndef CFG_TUD_VENDOR_RX_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
// TX FIFO can be disabled by setting this value to 0
|
||||
#ifndef CFG_TUD_VENDOR_TX_BUFSIZE
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 64
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_vendor_n_mounted (uint8_t itf);
|
||||
uint32_t tud_vendor_n_available (uint8_t itf);
|
||||
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
|
||||
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
|
||||
void tud_vendor_n_read_flush (uint8_t itf);
|
||||
|
||||
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
|
||||
uint32_t tud_vendor_n_write_flush (uint8_t itf);
|
||||
uint32_t tud_vendor_n_write_available (uint8_t itf);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port) i.e CFG_TUD_VENDOR = 1
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t itf, char const* str) {
|
||||
return tud_vendor_n_write(itf, str, strlen(str));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_mounted(void) {
|
||||
return tud_vendor_n_mounted(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_available(void) {
|
||||
return tud_vendor_n_available(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) {
|
||||
return tud_vendor_n_read(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t* ui8) {
|
||||
return tud_vendor_n_peek(0, ui8);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) {
|
||||
tud_vendor_n_read_flush(0);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(void const* buffer, uint32_t bufsize) {
|
||||
return tud_vendor_n_write(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(char const* str) {
|
||||
return tud_vendor_n_write_str(0, str);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) {
|
||||
return tud_vendor_n_write_flush(0);
|
||||
}
|
||||
|
||||
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
|
||||
return tud_vendor_n_write_available(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_flush() tud_vendor_write_flush()
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
|
||||
// Invoked when last rx transfer finished
|
||||
void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void);
|
||||
bool vendord_deinit(void);
|
||||
void vendord_reset(uint8_t rhport);
|
||||
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_VENDOR_DEVICE_H_ */
|
||||
146
managed_components/espressif__tinyusb/src/class/vendor/vendor_host.c
vendored
Normal file
146
managed_components/espressif__tinyusb/src/class/vendor/vendor_host.c
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_VENDOR)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "host/usbh.h"
|
||||
#include "vendor_host.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
custom_interface_info_t custom_interface[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
static tusb_error_t cush_validate_paras(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
|
||||
{
|
||||
if ( !tusbh_custom_is_mounted(dev_addr, vendor_id, product_id) )
|
||||
{
|
||||
return TUSB_ERROR_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
TU_ASSERT( p_buffer != NULL && length != 0, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API (need to check parameters)
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
|
||||
{
|
||||
TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_buffer, length) );
|
||||
|
||||
if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_in) )
|
||||
{
|
||||
return TUSB_ERROR_INTERFACE_IS_BUSY;
|
||||
}
|
||||
|
||||
(void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_in, p_buffer, length);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length)
|
||||
{
|
||||
TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_data, length) );
|
||||
|
||||
if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_out) )
|
||||
{
|
||||
return TUSB_ERROR_INTERFACE_IS_BUSY;
|
||||
}
|
||||
|
||||
(void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_out, p_data, length);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH-CLASS API
|
||||
//--------------------------------------------------------------------+
|
||||
void cush_init(void)
|
||||
{
|
||||
tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUH_DEVICE_MAX);
|
||||
}
|
||||
|
||||
tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
|
||||
{
|
||||
// FIXME quick hack to test lpc1k custom class with 2 bulk endpoints
|
||||
uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
//------------- Bulk Endpoints Descriptor -------------//
|
||||
for(uint32_t i=0; i<2; i++)
|
||||
{
|
||||
tusb_desc_endpoint_t const *p_endpoint = (tusb_desc_endpoint_t const *) p_desc;
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
(*p_length) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cush_close(uint8_t dev_addr)
|
||||
{
|
||||
tusb_error_t err1, err2;
|
||||
custom_interface_info_t * p_interface = &custom_interface[dev_addr-1];
|
||||
|
||||
// TODO re-consider to check pipe valid before calling pipe_close
|
||||
if( pipehandle_is_valid( p_interface->pipe_in ) )
|
||||
{
|
||||
err1 = hcd_pipe_close( p_interface->pipe_in );
|
||||
}
|
||||
|
||||
if ( pipehandle_is_valid( p_interface->pipe_out ) )
|
||||
{
|
||||
err2 = hcd_pipe_close( p_interface->pipe_out );
|
||||
}
|
||||
|
||||
tu_memclr(p_interface, sizeof(custom_interface_info_t));
|
||||
|
||||
TU_ASSERT(err1 == TUSB_ERROR_NONE && err2 == TUSB_ERROR_NONE, (void) 0 );
|
||||
}
|
||||
|
||||
#endif
|
||||
67
managed_components/espressif__tinyusb/src/class/vendor/vendor_host.h
vendored
Normal file
67
managed_components/espressif__tinyusb/src/class/vendor/vendor_host.h
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_VENDOR_HOST_H_
|
||||
#define _TUSB_VENDOR_HOST_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
pipe_handle_t pipe_in;
|
||||
pipe_handle_t pipe_out;
|
||||
}custom_interface_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
static inline bool tusbh_custom_is_mounted(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
(void) vendor_id; // TODO check this later
|
||||
(void) product_id;
|
||||
// return (tusbh_device_get_mounted_class_flag(dev_addr) & TU_BIT(TUSB_CLASS_MAPPED_INDEX_END-1) ) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length);
|
||||
bool tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void cush_init(void);
|
||||
bool cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
|
||||
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event);
|
||||
void cush_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_VENDOR_HOST_H_ */
|
||||
697
managed_components/espressif__tinyusb/src/class/video/video.h
Normal file
697
managed_components/espressif__tinyusb/src/class/video/video.h
Normal file
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Koji KITAYAMA
|
||||
*
|
||||
* 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_VIDEO_H_
|
||||
#define TUSB_VIDEO_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
enum {
|
||||
VIDEO_BCD_1_50 = 0x0150,
|
||||
};
|
||||
|
||||
// Table 3-19 Color Matching Descriptor
|
||||
typedef enum {
|
||||
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
|
||||
VIDEO_COLOR_PRIMARIES_BT709, // sRGB (default)
|
||||
VIDEO_COLOR_PRIMARIES_BT470_2M,
|
||||
VIDEO_COLOR_PRIMARIES_BT470_2BG,
|
||||
VIDEO_COLOR_PRIMARIES_SMPTE170M,
|
||||
VIDEO_COLOR_PRIMARIES_SMPTE240M,
|
||||
} video_color_primaries_t;
|
||||
|
||||
// Table 3-19 Color Matching Descriptor
|
||||
typedef enum {
|
||||
VIDEO_COLOR_XFER_CH_UNDEFINED = 0x00,
|
||||
VIDEO_COLOR_XFER_CH_BT709, // default
|
||||
VIDEO_COLOR_XFER_CH_BT470_2M,
|
||||
VIDEO_COLOR_XFER_CH_BT470_2BG,
|
||||
VIDEO_COLOR_XFER_CH_SMPTE170M,
|
||||
VIDEO_COLOR_XFER_CH_SMPTE240M,
|
||||
VIDEO_COLOR_XFER_CH_LINEAR,
|
||||
VIDEO_COLOR_XFER_CH_SRGB,
|
||||
} video_color_transfer_characteristics_t;
|
||||
|
||||
// Table 3-19 Color Matching Descriptor
|
||||
typedef enum {
|
||||
VIDEO_COLOR_COEF_UNDEFINED = 0x00,
|
||||
VIDEO_COLOR_COEF_BT709,
|
||||
VIDEO_COLOR_COEF_FCC,
|
||||
VIDEO_COLOR_COEF_BT470_2BG,
|
||||
VIDEO_COLOR_COEF_SMPTE170M, // BT.601 default
|
||||
VIDEO_COLOR_COEF_SMPTE240M,
|
||||
} video_color_matrix_coefficients_t;
|
||||
|
||||
/* 4.2.1.2 Request Error Code Control */
|
||||
typedef enum {
|
||||
VIDEO_ERROR_NONE = 0, /* The request succeeded. */
|
||||
VIDEO_ERROR_NOT_READY,
|
||||
VIDEO_ERROR_WRONG_STATE,
|
||||
VIDEO_ERROR_POWER,
|
||||
VIDEO_ERROR_OUT_OF_RANGE,
|
||||
VIDEO_ERROR_INVALID_UNIT,
|
||||
VIDEO_ERROR_INVALID_CONTROL,
|
||||
VIDEO_ERROR_INVALID_REQUEST,
|
||||
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE,
|
||||
VIDEO_ERROR_UNKNOWN = 0xFF,
|
||||
} video_error_code_t;
|
||||
|
||||
/* A.2 Interface Subclass */
|
||||
typedef enum {
|
||||
VIDEO_SUBCLASS_UNDEFINED = 0x00,
|
||||
VIDEO_SUBCLASS_CONTROL,
|
||||
VIDEO_SUBCLASS_STREAMING,
|
||||
VIDEO_SUBCLASS_INTERFACE_COLLECTION,
|
||||
} video_subclass_type_t;
|
||||
|
||||
/* A.3 Interface Protocol */
|
||||
typedef enum {
|
||||
VIDEO_ITF_PROTOCOL_UNDEFINED = 0x00,
|
||||
VIDEO_ITF_PROTOCOL_15,
|
||||
} video_interface_protocol_code_t;
|
||||
|
||||
/* A.5 Class-Specific VideoControl Interface Descriptor Subtypes */
|
||||
typedef enum {
|
||||
VIDEO_CS_ITF_VC_UNDEFINED = 0x00,
|
||||
VIDEO_CS_ITF_VC_HEADER,
|
||||
VIDEO_CS_ITF_VC_INPUT_TERMINAL,
|
||||
VIDEO_CS_ITF_VC_OUTPUT_TERMINAL,
|
||||
VIDEO_CS_ITF_VC_SELECTOR_UNIT,
|
||||
VIDEO_CS_ITF_VC_PROCESSING_UNIT,
|
||||
VIDEO_CS_ITF_VC_EXTENSION_UNIT,
|
||||
VIDEO_CS_ITF_VC_ENCODING_UNIT,
|
||||
VIDEO_CS_ITF_VC_MAX,
|
||||
} video_cs_vc_interface_subtype_t;
|
||||
|
||||
/* A.6 Class-Specific VideoStreaming Interface Descriptor Subtypes */
|
||||
typedef enum {
|
||||
VIDEO_CS_ITF_VS_UNDEFINED = 0x00,
|
||||
VIDEO_CS_ITF_VS_INPUT_HEADER = 0x01,
|
||||
VIDEO_CS_ITF_VS_OUTPUT_HEADER = 0x02,
|
||||
VIDEO_CS_ITF_VS_STILL_IMAGE_FRAME = 0x03,
|
||||
VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED = 0x04,
|
||||
VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED = 0x05,
|
||||
VIDEO_CS_ITF_VS_FORMAT_MJPEG = 0x06,
|
||||
VIDEO_CS_ITF_VS_FRAME_MJPEG = 0x07,
|
||||
VIDEO_CS_ITF_VS_FORMAT_MPEG2TS = 0x0A,
|
||||
VIDEO_CS_ITF_VS_FORMAT_DV = 0x0C,
|
||||
VIDEO_CS_ITF_VS_COLORFORMAT = 0x0D,
|
||||
VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED = 0x10,
|
||||
VIDEO_CS_ITF_VS_FRAME_FRAME_BASED = 0x11,
|
||||
VIDEO_CS_ITF_VS_FORMAT_STREAM_BASED = 0x12,
|
||||
VIDEO_CS_ITF_VS_FORMAT_H264 = 0x13,
|
||||
VIDEO_CS_ITF_VS_FRAME_H264 = 0x14,
|
||||
VIDEO_CS_ITF_VS_FORMAT_H264_SIMULCAST = 0x15,
|
||||
VIDEO_CS_ITF_VS_FORMAT_VP8 = 0x16,
|
||||
VIDEO_CS_ITF_VS_FRAME_VP8 = 0x17,
|
||||
VIDEO_CS_ITF_VS_FORMAT_VP8_SIMULCAST = 0x18,
|
||||
} video_cs_vs_interface_subtype_t;
|
||||
|
||||
/* A.7. Class-Specific Endpoint Descriptor Subtypes */
|
||||
typedef enum {
|
||||
VIDEO_CS_EP_UNDEFINED = 0x00,
|
||||
VIDEO_CS_EP_GENERAL,
|
||||
VIDEO_CS_EP_ENDPOINT,
|
||||
VIDEO_CS_EP_INTERRUPT
|
||||
} video_cs_ep_subtype_t;
|
||||
|
||||
/* A.8 Class-Specific Request Codes */
|
||||
typedef enum {
|
||||
VIDEO_REQUEST_UNDEFINED = 0x00,
|
||||
VIDEO_REQUEST_SET_CUR = 0x01,
|
||||
VIDEO_REQUEST_SET_CUR_ALL = 0x11,
|
||||
VIDEO_REQUEST_GET_CUR = 0x81,
|
||||
VIDEO_REQUEST_GET_MIN = 0x82,
|
||||
VIDEO_REQUEST_GET_MAX = 0x83,
|
||||
VIDEO_REQUEST_GET_RES = 0x84,
|
||||
VIDEO_REQUEST_GET_LEN = 0x85,
|
||||
VIDEO_REQUEST_GET_INFO = 0x86,
|
||||
VIDEO_REQUEST_GET_DEF = 0x87,
|
||||
VIDEO_REQUEST_GET_CUR_ALL = 0x91,
|
||||
VIDEO_REQUEST_GET_MIN_ALL = 0x92,
|
||||
VIDEO_REQUEST_GET_MAX_ALL = 0x93,
|
||||
VIDEO_REQUEST_GET_RES_ALL = 0x94,
|
||||
VIDEO_REQUEST_GET_DEF_ALL = 0x97
|
||||
} video_control_request_t;
|
||||
|
||||
/* A.9.1 VideoControl Interface Control Selectors */
|
||||
typedef enum {
|
||||
VIDEO_VC_CTL_UNDEFINED = 0x00,
|
||||
VIDEO_VC_CTL_VIDEO_POWER_MODE, // 0x01
|
||||
VIDEO_VC_CTL_REQUEST_ERROR_CODE, // 0x02
|
||||
} video_interface_control_selector_t;
|
||||
|
||||
/* A.9.8 VideoStreaming Interface Control Selectors */
|
||||
typedef enum {
|
||||
VIDEO_VS_CTL_UNDEFINED = 0x00,
|
||||
VIDEO_VS_CTL_PROBE, // 0x01
|
||||
VIDEO_VS_CTL_COMMIT, // 0x02
|
||||
VIDEO_VS_CTL_STILL_PROBE, // 0x03
|
||||
VIDEO_VS_CTL_STILL_COMMIT, // 0x04
|
||||
VIDEO_VS_CTL_STILL_IMAGE_TRIGGER, // 0x05
|
||||
VIDEO_VS_CTL_STREAM_ERROR_CODE, // 0x06
|
||||
VIDEO_VS_CTL_GENERATE_KEY_FRAME, // 0x07
|
||||
VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT, // 0x08
|
||||
VIDEO_VS_CTL_SYNCH_DELAY_CONTROL, // 0x09
|
||||
|
||||
} video_interface_streaming_selector_t;
|
||||
|
||||
/* B. Terminal Types */
|
||||
typedef enum {
|
||||
// Terminal
|
||||
VIDEO_TT_VENDOR_SPECIFIC = 0x0100,
|
||||
VIDEO_TT_STREAMING = 0x0101,
|
||||
|
||||
// Input
|
||||
VIDEO_ITT_VENDOR_SPECIFIC = 0x0200,
|
||||
VIDEO_ITT_CAMERA = 0x0201,
|
||||
VIDEO_ITT_MEDIA_TRANSPORT_INPUT = 0x0202,
|
||||
|
||||
// Output
|
||||
VIDEO_OTT_VENDOR_SPECIFIC = 0x0300,
|
||||
VIDEO_OTT_DISPLAY = 0x0301,
|
||||
VIDEO_OTT_MEDIA_TRANSPORT_OUTPUT = 0x0302,
|
||||
|
||||
// External
|
||||
VIDEO_ETT_VENDOR_SPEIFIC = 0x0400,
|
||||
VIDEO_ETT_COMPOSITE_CONNECTOR = 0x0401,
|
||||
VIDEO_ETT_SVIDEO_CONNECTOR = 0x0402,
|
||||
VIDEO_ETT_COMPONENT_CONNECTOR = 0x0403,
|
||||
} video_terminal_type_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Video Control (VC) Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 2.3.4.2 */
|
||||
#define tusb_desc_video_control_header_nitf_t(_nitf) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint16_t bcdUVC; \
|
||||
uint16_t wTotalLength; \
|
||||
uint32_t dwClockFrequency; /* deprecated */ \
|
||||
uint8_t bInCollection; \
|
||||
uint8_t baInterfaceNr[_nitf]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
|
||||
typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
} tusb_desc_video_control_input_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t bSourceID;
|
||||
uint8_t iTerminal;
|
||||
} tusb_desc_video_control_output_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct");
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bTerminalID;
|
||||
uint16_t wTerminalType;
|
||||
uint8_t bAssocTerminal;
|
||||
uint8_t iTerminal;
|
||||
|
||||
uint16_t wObjectiveFocalLengthMin;
|
||||
uint16_t wObjectiveFocalLengthMax;
|
||||
uint16_t wOcularFocalLength;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmControls[3];
|
||||
} tusb_desc_video_control_camera_terminal_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Video Streaming (VS) Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 3.9.2.1 */
|
||||
#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \
|
||||
uint16_t wTotalLength; \
|
||||
uint8_t bEndpointAddress; \
|
||||
uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \
|
||||
uint8_t bTerminalLink; \
|
||||
uint8_t bStillCaptureMethod; \
|
||||
uint8_t bTriggerSupport; /* Hardware trigger supported */ \
|
||||
uint8_t bTriggerUsage; \
|
||||
uint8_t bControlSize; /* sizeof of each control item */ \
|
||||
uint8_t bmaControls[_nb]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t;
|
||||
typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t;
|
||||
|
||||
/* 3.9.2.2 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bNumFormats;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls[];
|
||||
} tusb_desc_video_streaming_output_header_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bNumFormats;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bEndpointAddress;
|
||||
union {
|
||||
struct {
|
||||
uint8_t bmInfo;
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bStillCaptureMethod;
|
||||
uint8_t bTriggerSupport;
|
||||
uint8_t bTriggerUsage;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls[];
|
||||
} input;
|
||||
struct {
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bTerminalLink;
|
||||
uint8_t bControlSize;
|
||||
uint8_t bmaControls[];
|
||||
} output;
|
||||
};
|
||||
} tusb_desc_video_streaming_inout_header_t;
|
||||
|
||||
// 3.9.2.6 Color Matching Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bColorPrimaries;
|
||||
uint8_t bTransferCharacteristics;
|
||||
uint8_t bMatrixCoefficients;
|
||||
} tusb_desc_video_streaming_color_matching_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Format and Frame Descriptor
|
||||
// Note: bFormatIndex & bFrameIndex are 1-based index
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Uncompressed -------------//
|
||||
// Uncompressed payload specs: 3.1.1 format descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format
|
||||
uint8_t guidFormat[16];
|
||||
uint8_t bBitsPerPixel;
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
} tusb_desc_video_format_uncompressed_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_uncompressed_t) == 27, "size is not correct");
|
||||
|
||||
// Uncompressed payload specs: 3.1.2 frame descriptor
|
||||
#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bFrameIndex; \
|
||||
uint8_t bmCapabilities; \
|
||||
uint16_t wWidth; \
|
||||
uint16_t wHeight; \
|
||||
uint32_t dwMinBitRate; \
|
||||
uint32_t dwMaxBitRate; \
|
||||
uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \
|
||||
uint32_t dwDefaultFrameInterval; /* 100ns unit */\
|
||||
uint8_t bFrameIntervalType; \
|
||||
uint32_t dwFrameInterval[_nint]; \
|
||||
}
|
||||
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t;
|
||||
|
||||
// continuous = 3 intervals: min, max, step
|
||||
typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct");
|
||||
|
||||
//------------- MJPEG -------------//
|
||||
// MJPEG payload specs: 3.1.1 format descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes)
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
} tusb_desc_video_format_mjpeg_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_mjpeg_t) == 11, "size is not correct");
|
||||
|
||||
// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed)
|
||||
typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t;
|
||||
typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t;
|
||||
|
||||
// continuous = 3 intervals: min, max, step
|
||||
typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t;
|
||||
|
||||
//------------- DV -------------//
|
||||
// DV payload specs: 3.1.1
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
|
||||
uint8_t bFormatType;
|
||||
} tusb_desc_video_format_dv_t;
|
||||
|
||||
// Frame Based payload specs: 3.1.1
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
uint8_t guidFormat[16];
|
||||
uint8_t bBitsPerPixel;
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
uint8_t bVaribaleSize;
|
||||
} tusb_desc_video_format_framebased_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bDescriptorSubType;
|
||||
uint8_t bFrameIndex;
|
||||
uint8_t bmCapabilities;
|
||||
uint16_t wWidth;
|
||||
uint16_t wHeight;
|
||||
uint32_t dwMinBitRate;
|
||||
uint32_t dwMaxBitRate;
|
||||
uint32_t dwDefaultFrameInterval;
|
||||
uint8_t bFrameIntervalType;
|
||||
uint32_t dwBytesPerLine;
|
||||
uint32_t dwFrameInterval[];
|
||||
} tusb_desc_video_frame_framebased_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Requests
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* 2.4.3.3 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bHeaderLength;
|
||||
union {
|
||||
uint8_t bmHeaderInfo;
|
||||
struct {
|
||||
uint8_t FrameID: 1;
|
||||
uint8_t EndOfFrame: 1;
|
||||
uint8_t PresentationTime: 1;
|
||||
uint8_t SourceClockReference: 1;
|
||||
uint8_t PayloadSpecific: 1;
|
||||
uint8_t StillImage: 1;
|
||||
uint8_t Error: 1;
|
||||
uint8_t EndOfHeader: 1;
|
||||
};
|
||||
};
|
||||
} tusb_video_payload_header_t;
|
||||
|
||||
/* 4.3.1.1 */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
union {
|
||||
uint8_t bmHint;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t dwFrameInterval: 1;
|
||||
uint16_t wKeyFrameRatel : 1;
|
||||
uint16_t wPFrameRate : 1;
|
||||
uint16_t wCompQuality : 1;
|
||||
uint16_t wCompWindowSize: 1;
|
||||
uint16_t : 0;
|
||||
} Hint;
|
||||
};
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bFrameIndex;
|
||||
uint32_t dwFrameInterval;
|
||||
uint16_t wKeyFrameRate;
|
||||
uint16_t wPFrameRate;
|
||||
uint16_t wCompQuality;
|
||||
uint16_t wCompWindowSize;
|
||||
uint16_t wDelay;
|
||||
uint32_t dwMaxVideoFrameSize;
|
||||
uint32_t dwMaxPayloadTransferSize;
|
||||
uint32_t dwClockFrequency;
|
||||
union {
|
||||
uint8_t bmFramingInfo;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t FrameID : 1;
|
||||
uint8_t EndOfFrame: 1;
|
||||
uint8_t EndOfSlice: 1;
|
||||
uint8_t : 0;
|
||||
} FramingInfo;
|
||||
};
|
||||
uint8_t bPreferedVersion;
|
||||
uint8_t bMinVersion;
|
||||
uint8_t bMaxVersion;
|
||||
uint8_t bUsage;
|
||||
uint8_t bBitDepthLuma;
|
||||
uint8_t bmSettings;
|
||||
uint8_t bMaxNumberOfRefFramesPlus1;
|
||||
uint16_t bmRateControlModes;
|
||||
uint64_t bmLayoutPerStream;
|
||||
} video_probe_and_commit_control_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not correct");
|
||||
|
||||
#define TUD_VIDEO_DESC_IAD_LEN 8
|
||||
#define TUD_VIDEO_DESC_STD_VC_LEN 9
|
||||
#define TUD_VIDEO_DESC_CS_VC_LEN 12
|
||||
#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8
|
||||
#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9
|
||||
#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18
|
||||
#define TUD_VIDEO_DESC_STD_VS_LEN 9
|
||||
#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13
|
||||
#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN 28
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN 38
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN 26
|
||||
#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6
|
||||
|
||||
/* 2.2 compression formats */
|
||||
#define TUD_VIDEO_GUID_YUY2 0x59,0x55,0x59,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||
#define TUD_VIDEO_GUID_NV12 0x4E,0x56,0x31,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||
#define TUD_VIDEO_GUID_M420 0x4D,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||
#define TUD_VIDEO_GUID_I420 0x49,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||
#define TUD_VIDEO_GUID_H264 0x48,0x32,0x36,0x34,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||
|
||||
#define TUD_VIDEO_DESC_IAD(_firstitf, _nitfs, _stridx) \
|
||||
TUD_VIDEO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, \
|
||||
_firstitf, _nitfs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_INTERFACE_COLLECTION, \
|
||||
VIDEO_ITF_PROTOCOL_UNDEFINED, _stridx
|
||||
|
||||
#define TUD_VIDEO_DESC_STD_VC(_itfnum, _nEPs, _stridx) \
|
||||
TUD_VIDEO_DESC_STD_VC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, \
|
||||
_nEPs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_CONTROL, VIDEO_ITF_PROTOCOL_15, _stridx
|
||||
|
||||
/* 3.7.2 */
|
||||
#define TUD_VIDEO_DESC_CS_VC(_bcdUVC, _totallen, _clkfreq, ...) \
|
||||
TUD_VIDEO_DESC_CS_VC_LEN + (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_HEADER, \
|
||||
U16_TO_U8S_LE(_bcdUVC), U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VC_LEN + (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||
U32_TO_U8S_LE(_clkfreq), TU_ARGS_NUM(__VA_ARGS__), __VA_ARGS__
|
||||
|
||||
/* 3.7.2.1 */
|
||||
#define TUD_VIDEO_DESC_INPUT_TERM(_tid, _tt, _at, _stridx) \
|
||||
TUD_VIDEO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_INPUT_TERMINAL, \
|
||||
_tid, U16_TO_U8S_LE(_tt), _at, _stridx
|
||||
|
||||
/* 3.7.2.2 */
|
||||
#define TUD_VIDEO_DESC_OUTPUT_TERM(_tid, _tt, _at, _srcid, _stridx) \
|
||||
TUD_VIDEO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_OUTPUT_TERMINAL, \
|
||||
_tid, U16_TO_U8S_LE(_tt), _at, _srcid, _stridx
|
||||
|
||||
/* 3.7.2.3 */
|
||||
#define TUD_VIDEO_DESC_CAMERA_TERM(_tid, _at, _stridx, _focal_min, _focal_max, _focal, _ctls) \
|
||||
TUD_VIDEO_DESC_CAMERA_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_INPUT_TERMINAL, \
|
||||
_tid, U16_TO_U8S_LE(VIDEO_ITT_CAMERA), _at, _stridx, \
|
||||
U16_TO_U8S_LE(_focal_min), U16_TO_U8S_LE(_focal_max), U16_TO_U8S_LE(_focal), 3, \
|
||||
TU_U32_BYTE0(_ctls), TU_U32_BYTE1(_ctls), TU_U32_BYTE2(_ctls)
|
||||
|
||||
/* 3.9.1 */
|
||||
#define TUD_VIDEO_DESC_STD_VS(_itfnum, _alt, _epn, _stridx) \
|
||||
TUD_VIDEO_DESC_STD_VS_LEN, TUSB_DESC_INTERFACE, _itfnum, _alt, \
|
||||
_epn, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_STREAMING, VIDEO_ITF_PROTOCOL_15, _stridx
|
||||
|
||||
/* 3.9.2.1 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_INPUT(_numfmt, _totallen, _ep, _inf, _termlnk, _sticaptmeth, _trgspt, _trgusg, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_IN_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, \
|
||||
VIDEO_CS_ITF_VS_INPUT_HEADER, _numfmt, \
|
||||
U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VS_IN_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||
_ep, _inf, _termlnk, _sticaptmeth, _trgspt, _trgusg, (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
/* 3.9.2.2 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_OUTPUT(_numfmt, _totallen, _ep, _inf, _termlnk, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_OUT_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, \
|
||||
VIDEO_CS_ITF_VS_OUTPUT_HEADER, _numfmt, \
|
||||
U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VS_OUT_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||
_ep, _inf, _termlnk, (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
/* Uncompressed 3.1.1 */
|
||||
#define TUD_VIDEO_GUID(_g0,_g1,_g2,_g3,_g4,_g5,_g6,_g7,_g8,_g9,_g10,_g11,_g12,_g13,_g14,_g15) _g0,_g1,_g2,_g3,_g4,_g5,_g6,_g7,_g8,_g9,_g10,_g11,_g12,_g13,_g14,_g15
|
||||
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfrmdesc, \
|
||||
_guid, _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp) \
|
||||
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, \
|
||||
_fmtidx, _numfrmdesc, TUD_VIDEO_GUID(_guid), \
|
||||
_bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp
|
||||
|
||||
/* Uncompressed 3.1.2 Table 3-3 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \
|
||||
U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
|
||||
|
||||
/* Uncompressed 3.1.2 Table 3-4 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
/* Motion-JPEG 3.1.1 Table 3-1 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp) \
|
||||
TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_MJPEG, \
|
||||
_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp
|
||||
|
||||
/* Motion-JPEG 3.1.1 Table 3-2 and 3-3 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \
|
||||
U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
|
||||
|
||||
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
/* Motion-Frame-Based 3.1.1 Table 3-1 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED(_fmtidx, _numfrmdesc, _guid, _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize) \
|
||||
TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED, \
|
||||
_fmtidx, _numfrmdesc, TUD_VIDEO_GUID(_guid), _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize
|
||||
|
||||
/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-3 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, _minfrminterval, _maxfrminterval, _frmintervalstep) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_frminterval), 0, U32_TO_U8S_LE(_bytesperline), \
|
||||
U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
|
||||
|
||||
/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-4 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, ...) \
|
||||
TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \
|
||||
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||
U32_TO_U8S_LE(_frminterval), U32_TO_U8S_LE(_bytesperline), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||
|
||||
/* 3.9.2.6 */
|
||||
#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \
|
||||
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \
|
||||
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_COLORFORMAT, \
|
||||
_color, _trns, _mat
|
||||
|
||||
/* 3.10.1.1 */
|
||||
#define TUD_VIDEO_DESC_EP_ISO(_ep, _epsize, _ep_interval) \
|
||||
7, TUSB_DESC_ENDPOINT, _ep, (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS),\
|
||||
U16_TO_U8S_LE(_epsize), _ep_interval
|
||||
|
||||
/* 3.10.1.2 */
|
||||
#define TUD_VIDEO_DESC_EP_BULK(_ep, _epsize, _ep_interval) \
|
||||
7, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), _ep_interval
|
||||
|
||||
#endif
|
||||
1447
managed_components/espressif__tinyusb/src/class/video/video_device.c
Normal file
1447
managed_components/espressif__tinyusb/src/class/video/video_device.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2021 Koji KITAYAMA
|
||||
*
|
||||
* 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_VIDEO_DEVICE_H_
|
||||
#define TUSB_VIDEO_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "video.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Ports)
|
||||
// CFG_TUD_VIDEO > 1
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tud_video_n_connected(uint_fast8_t ctl_idx);
|
||||
|
||||
/** Return true if streaming
|
||||
*
|
||||
* @param[in] ctl_idx Destination control interface index
|
||||
* @param[in] stm_idx Destination streaming interface index */
|
||||
bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
|
||||
|
||||
/** Transfer a frame
|
||||
*
|
||||
* @param[in] ctl_idx Destination control interface index
|
||||
* @param[in] stm_idx Destination streaming interface index
|
||||
* @param[in] buffer Frame buffer. The caller must not use this buffer until the operation is completed.
|
||||
* @param[in] bufsize Byte size of the frame buffer */
|
||||
bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize);
|
||||
|
||||
/*------------- Optional callbacks -------------*/
|
||||
/** Invoked when compeletion of a frame transfer
|
||||
*
|
||||
* @param[in] ctl_idx Destination control interface index
|
||||
* @param[in] stm_idx Destination streaming interface index */
|
||||
void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/** Invoked when SET_POWER_MODE request received
|
||||
*
|
||||
* @param[in] ctl_idx Destination control interface index
|
||||
* @param[in] stm_idx Destination streaming interface index
|
||||
* @return video_error_code_t */
|
||||
int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
|
||||
|
||||
/** Invoked when VS_COMMIT_CONTROL(SET_CUR) request received
|
||||
*
|
||||
* @param[in] ctl_idx Destination control interface index
|
||||
* @param[in] stm_idx Destination streaming interface index
|
||||
* @param[in] parameters Video streaming parameters
|
||||
* @return video_error_code_t */
|
||||
int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
|
||||
video_probe_and_commit_control_t const *parameters);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void videod_init (void);
|
||||
bool videod_deinit (void);
|
||||
void videod_reset (uint8_t rhport);
|
||||
uint16_t videod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool videod_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
391
managed_components/espressif__tinyusb/src/common/tusb_common.h
Normal file
391
managed_components/espressif__tinyusb/src/common/tusb_common.h
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_COMMON_H_
|
||||
#define _TUSB_COMMON_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Macros Helper
|
||||
//--------------------------------------------------------------------+
|
||||
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
|
||||
#define TU_FIELD_SZIE(_type, _field) (sizeof(((_type *)0)->_field))
|
||||
#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
|
||||
#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
|
||||
#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low)))
|
||||
#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff))
|
||||
#define TU_U16_LOW(_u16) ((uint8_t) ((_u16) & 0x00ff))
|
||||
#define U16_TO_U8S_BE(_u16) TU_U16_HIGH(_u16), TU_U16_LOW(_u16)
|
||||
#define U16_TO_U8S_LE(_u16) TU_U16_LOW(_u16), TU_U16_HIGH(_u16)
|
||||
|
||||
#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB
|
||||
#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff))
|
||||
#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff))
|
||||
#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB
|
||||
|
||||
#define U32_TO_U8S_BE(_u32) TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32)
|
||||
#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32)
|
||||
|
||||
#define TU_BIT(n) (1UL << (n))
|
||||
|
||||
// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111
|
||||
#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Includes
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Standard Headers
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Tinyusb Common Headers
|
||||
#include "tusb_option.h"
|
||||
#include "tusb_compiler.h"
|
||||
#include "tusb_verify.h"
|
||||
#include "tusb_types.h"
|
||||
#include "tusb_debug.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// API implemented by application if needed
|
||||
// TODO move to a more obvious place/file
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get current milliseconds, required by some port/configuration without RTOS
|
||||
extern uint32_t tusb_time_millis_api(void);
|
||||
|
||||
// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS
|
||||
extern void tusb_time_delay_ms_api(uint32_t ms);
|
||||
|
||||
// flush data cache
|
||||
extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);
|
||||
|
||||
// invalidate data cache
|
||||
extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);
|
||||
|
||||
// Optional physical <-> virtual address translation
|
||||
extern void* tusb_app_virt_to_phys(void *virt_addr);
|
||||
extern void* tusb_app_phys_to_virt(void *phys_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Mem -------------//
|
||||
#define tu_memclr(buffer, size) memset((buffer), 0, (size))
|
||||
#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
|
||||
|
||||
// This is a backport of memset_s from c11
|
||||
TU_ATTR_ALWAYS_INLINE static inline int tu_memset_s(void *dest, size_t destsz, int ch, size_t count) {
|
||||
// Validate parameters
|
||||
if (dest == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count > destsz) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(dest, ch, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is a backport of memcpy_s from c11
|
||||
TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count) {
|
||||
// Validate parameters
|
||||
if (dest == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// For memcpy, src may be NULL only if count == 0. Reject otherwise.
|
||||
if (src == NULL && count != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count > destsz) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(dest, src, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_mem_is_zero(const void *buffer, size_t size) {
|
||||
const uint8_t* buf8 = (const uint8_t*) buffer;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (buf8[i] != 0) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_mem_is_ff(const void *buffer, size_t size) {
|
||||
const uint8_t* buf8 = (const uint8_t*) buffer;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (buf8[i] != 0xff) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------- Bytes -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) {
|
||||
return (((uint32_t)b3) << 24) | (((uint32_t)b2) << 16) | (((uint32_t)b1) << 8) | b0;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32_from_u16(uint16_t high, uint16_t low) {
|
||||
return (((uint32_t)high) << 16) | low;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) {
|
||||
return (uint16_t)((((uint16_t)high) << 8) | low);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t ui32) { return TU_U32_BYTE2(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t ui32) { return TU_U32_BYTE1(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t ui32) { return TU_U32_BYTE0(ui32); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_U16_HIGH(ui16); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); }
|
||||
|
||||
//------------- Bits -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; }
|
||||
|
||||
//------------- Min -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; }
|
||||
|
||||
//------------- Max -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
|
||||
|
||||
//------------- Align -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) {
|
||||
return value & ((uint32_t) ~(alignment-1));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { return (value & 0x1FUL) == 0; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; }
|
||||
|
||||
//------------- Mathematics -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return TU_DIV_CEIL(v, d); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_round_up(uint32_t v, uint32_t f) { return tu_div_ceil(v, f) * f; }
|
||||
|
||||
// log2 of a value is its MSB's position
|
||||
// TODO use clz TODO remove
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_log2(uint32_t value) {
|
||||
uint8_t result = 0;
|
||||
while (value >>= 1) { result++; }
|
||||
return result;
|
||||
}
|
||||
|
||||
//static inline uint8_t tu_log2(uint32_t value)
|
||||
//{
|
||||
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
|
||||
//}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_power_of_two(uint32_t value) {
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
//------------- Unaligned Access -------------//
|
||||
#if TUP_ARCH_STRICT_ALIGN
|
||||
|
||||
// Rely on compiler to generate correct code for unaligned access
|
||||
typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t;
|
||||
typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) {
|
||||
tu_unaligned_uint32_t const *ua32 = (tu_unaligned_uint32_t const *) mem;
|
||||
return ua32->val;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) {
|
||||
tu_unaligned_uint32_t *ua32 = (tu_unaligned_uint32_t *) mem;
|
||||
ua32->val = value;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) {
|
||||
tu_unaligned_uint16_t const *ua16 = (tu_unaligned_uint16_t const *) mem;
|
||||
return ua16->val;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) {
|
||||
tu_unaligned_uint16_t *ua16 = (tu_unaligned_uint16_t *) mem;
|
||||
ua16->val = value;
|
||||
}
|
||||
|
||||
#elif TUP_MCU_STRICT_ALIGN
|
||||
|
||||
// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4.
|
||||
// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code
|
||||
// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code
|
||||
// TODO Big Endian may need minor changes
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
|
||||
{
|
||||
volatile uint8_t const* buf8 = (uint8_t const*) mem;
|
||||
return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
|
||||
{
|
||||
volatile uint8_t* buf8 = (uint8_t*) mem;
|
||||
buf8[0] = tu_u32_byte0(value);
|
||||
buf8[1] = tu_u32_byte1(value);
|
||||
buf8[2] = tu_u32_byte2(value);
|
||||
buf8[3] = tu_u32_byte3(value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
|
||||
{
|
||||
volatile uint8_t const* buf8 = (uint8_t const*) mem;
|
||||
return tu_u16(buf8[1], buf8[0]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
|
||||
{
|
||||
volatile uint8_t* buf8 = (uint8_t*) mem;
|
||||
buf8[0] = tu_u16_low(value);
|
||||
buf8[1] = tu_u16_high(value);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
// MCU that could access unaligned memory natively
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) {
|
||||
return *((uint32_t const *) mem);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) {
|
||||
return *((uint16_t const *) mem);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) {
|
||||
*((uint32_t *) mem) = value;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) {
|
||||
*((uint16_t *) mem) = value;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// To be removed
|
||||
//------------- Binary constant -------------//
|
||||
#if defined(__GNUC__) && !defined(__CC_ARM)
|
||||
|
||||
#define TU_BIN8(x) ((uint8_t) (0b##x))
|
||||
#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
|
||||
#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
|
||||
|
||||
#else
|
||||
|
||||
// internal macro of B8, B16, B32
|
||||
#define _B8__(x) (((x&0x0000000FUL)?1:0) \
|
||||
+((x&0x000000F0UL)?2:0) \
|
||||
+((x&0x00000F00UL)?4:0) \
|
||||
+((x&0x0000F000UL)?8:0) \
|
||||
+((x&0x000F0000UL)?16:0) \
|
||||
+((x&0x00F00000UL)?32:0) \
|
||||
+((x&0x0F000000UL)?64:0) \
|
||||
+((x&0xF0000000UL)?128:0))
|
||||
|
||||
#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL))
|
||||
#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb))
|
||||
#define TU_BIN32(dmsb,db2,db3,dlsb) \
|
||||
(((uint32_t)TU_BIN8(dmsb)<<24) \
|
||||
+ ((uint32_t)TU_BIN8(db2)<<16) \
|
||||
+ ((uint32_t)TU_BIN8(db3)<<8) \
|
||||
+ TU_BIN8(dlsb))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptor helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// return next descriptor
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
|
||||
uint8_t const* desc8 = (uint8_t const*) desc;
|
||||
return desc8 + desc8[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
// get descriptor length
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
// get descriptor type
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
|
||||
}
|
||||
|
||||
// get descriptor subtype
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_subtype(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_SUBTYPE];
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_in_bounds(uint8_t const* p_desc, uint8_t const* desc_end) {
|
||||
return (p_desc < desc_end) && (tu_desc_next(p_desc) <= desc_end);
|
||||
}
|
||||
|
||||
// find descriptor that match byte1 (type)
|
||||
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1);
|
||||
|
||||
// find descriptor that match byte1 (type) and byte2
|
||||
uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2);
|
||||
|
||||
// find descriptor that match byte1 (type) and byte2
|
||||
uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_COMMON_H_ */
|
||||
310
managed_components/espressif__tinyusb/src/common/tusb_compiler.h
Normal file
310
managed_components/espressif__tinyusb/src/common/tusb_compiler.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup Group_Common
|
||||
* \defgroup Group_Compiler Compiler
|
||||
* \brief Group_Compiler brief
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_COMPILER_H_
|
||||
#define _TUSB_COMPILER_H_
|
||||
|
||||
#define TU_TOKEN(x) x
|
||||
#define TU_STRING(x) #x ///< stringify without expand
|
||||
#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
|
||||
|
||||
#define TU_STRCAT(a, b) a##b ///< concat without expand
|
||||
#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand
|
||||
|
||||
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
|
||||
#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens
|
||||
|
||||
#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) )
|
||||
|
||||
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
|
||||
#define _TU_COUNTER_ __COUNTER__
|
||||
#else
|
||||
#define _TU_COUNTER_ __LINE__
|
||||
#endif
|
||||
|
||||
// Compile-time Assert
|
||||
#if defined (__cplusplus) && __cplusplus >= 201103L
|
||||
#define TU_VERIFY_STATIC static_assert
|
||||
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define TU_VERIFY_STATIC _Static_assert
|
||||
#elif defined(__CCRX__)
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
|
||||
#else
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
|
||||
#endif
|
||||
|
||||
/* --------------------- Fuzzing types -------------------------------------- */
|
||||
#ifdef _FUZZ
|
||||
#define tu_static static __thread
|
||||
#else
|
||||
#define tu_static static
|
||||
#endif
|
||||
|
||||
// for declaration of reserved field, make use of _TU_COUNTER_
|
||||
#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
|
||||
|
||||
#define TU_LITTLE_ENDIAN (0x12u)
|
||||
#define TU_BIG_ENDIAN (0x21u)
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Count number of arguments of __VA_ARGS__
|
||||
* - reference https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments
|
||||
* - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
|
||||
* - _RSEQ_N() is reverse sequential to N to add padding to have
|
||||
* Nth position is the same as the number of arguments
|
||||
* - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
|
||||
*------------------------------------------------------------------*/
|
||||
#if !defined(__CCRX__)
|
||||
#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N())
|
||||
#else
|
||||
#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N())
|
||||
#endif
|
||||
|
||||
#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__)
|
||||
#define _GET_NTH_ARG( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
|
||||
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
|
||||
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
|
||||
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
|
||||
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
|
||||
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
|
||||
_61,_62,_63,N,...) N
|
||||
#define _RSEQ_N() \
|
||||
62,61,60, \
|
||||
59,58,57,56,55,54,53,52,51,50, \
|
||||
49,48,47,46,45,44,43,42,41,40, \
|
||||
39,38,37,36,35,34,33,32,31,30, \
|
||||
29,28,27,26,25,24,23,22,21,20, \
|
||||
19,18,17,16,15,14,13,12,11,10, \
|
||||
9,8,7,6,5,4,3,2,1,0
|
||||
|
||||
// Apply an macro X to each of the arguments with an separated of choice
|
||||
#define TU_ARGS_APPLY(_X, _s, ...) TU_XSTRCAT(_TU_ARGS_APPLY_, TU_ARGS_NUM(__VA_ARGS__))(_X, _s, __VA_ARGS__)
|
||||
|
||||
#define _TU_ARGS_APPLY_1(_X, _s, _a1) _X(_a1)
|
||||
#define _TU_ARGS_APPLY_2(_X, _s, _a1, _a2) _X(_a1) _s _X(_a2)
|
||||
#define _TU_ARGS_APPLY_3(_X, _s, _a1, _a2, _a3) _X(_a1) _s _TU_ARGS_APPLY_2(_X, _s, _a2, _a3)
|
||||
#define _TU_ARGS_APPLY_4(_X, _s, _a1, _a2, _a3, _a4) _X(_a1) _s _TU_ARGS_APPLY_3(_X, _s, _a2, _a3, _a4)
|
||||
#define _TU_ARGS_APPLY_5(_X, _s, _a1, _a2, _a3, _a4, _a5) _X(_a1) _s _TU_ARGS_APPLY_4(_X, _s, _a2, _a3, _a4, _a5)
|
||||
#define _TU_ARGS_APPLY_6(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1) _s _TU_ARGS_APPLY_5(_X, _s, _a2, _a3, _a4, _a5, _a6)
|
||||
#define _TU_ARGS_APPLY_7(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1) _s _TU_ARGS_APPLY_6(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7)
|
||||
#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Macro for function default arguments
|
||||
//--------------------------------------------------------------------+
|
||||
#define TU_GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
|
||||
|
||||
// function expand with number of arguments
|
||||
#define TU_FUNC_OPTIONAL_ARG(func, ...) TU_XSTRCAT(func##_arg, TU_ARGS_NUM(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Compiler porting with Attribute and Endian
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TODO refactor since __attribute__ is supported across many compiler
|
||||
#if defined(__GNUC__)
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
|
||||
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#endif
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
#if __GNUC__ < 5
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
#else
|
||||
#if __has_attribute(__fallthrough__)
|
||||
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
#else
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
// Unfortunately XC16 doesn't provide builtins for 32bit endian conversion
|
||||
#if defined(__XC16)
|
||||
#define TU_BSWAP16(u16) (__builtin_swap(u16))
|
||||
#define TU_BSWAP32(u32) ((((u32) & 0xff000000) >> 24) | \
|
||||
(((u32) & 0x00ff0000) >> 8) | \
|
||||
(((u32) & 0x0000ff00) << 8) | \
|
||||
(((u32) & 0x000000ff) << 24))
|
||||
#else
|
||||
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
|
||||
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
|
||||
#endif
|
||||
|
||||
#ifndef __ARMCC_VERSION
|
||||
// List of obsolete callback function that is renamed and should not be defined.
|
||||
// Put it here since only gcc support this pragma
|
||||
#pragma GCC poison tud_vendor_control_request_cb
|
||||
#endif
|
||||
|
||||
#elif defined(__TI_COMPILER_VERSION__)
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used))
|
||||
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
// __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian)
|
||||
#if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__)
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
|
||||
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
|
||||
|
||||
#elif defined(__ICCARM__)
|
||||
#include <intrinsics.h>
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
|
||||
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#endif
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
|
||||
#define TU_BSWAP32(u32) (__iar_builtin_REV(u32))
|
||||
|
||||
#elif defined(__CCRX__)
|
||||
#define TU_ATTR_ALIGNED(Bytes)
|
||||
#define TU_ATTR_SECTION(sec_name)
|
||||
#define TU_ATTR_PACKED
|
||||
#define TU_ATTR_WEAK
|
||||
// #define TU_ATTR_WEAK_ALIAS(f)
|
||||
#define TU_ATTR_ALWAYS_INLINE
|
||||
#define TU_ATTR_DEPRECATED(mess)
|
||||
#define TU_ATTR_UNUSED
|
||||
#define TU_ATTR_USED
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN _Pragma("pack")
|
||||
#define TU_ATTR_PACKED_END _Pragma("packoption")
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right")
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order")
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if defined(__LIT)
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16))
|
||||
#define TU_BSWAP32(u32) (_builtin_revl(u32))
|
||||
|
||||
#else
|
||||
#error "Compiler attribute porting is required"
|
||||
#endif
|
||||
|
||||
|
||||
#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN)
|
||||
|
||||
#define tu_htons(u16) (TU_BSWAP16(u16))
|
||||
#define tu_ntohs(u16) (TU_BSWAP16(u16))
|
||||
|
||||
#define tu_htonl(u32) (TU_BSWAP32(u32))
|
||||
#define tu_ntohl(u32) (TU_BSWAP32(u32))
|
||||
|
||||
#define tu_htole16(u16) (u16)
|
||||
#define tu_le16toh(u16) (u16)
|
||||
|
||||
#define tu_htole32(u32) (u32)
|
||||
#define tu_le32toh(u32) (u32)
|
||||
|
||||
#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN)
|
||||
|
||||
#define tu_htons(u16) (u16)
|
||||
#define tu_ntohs(u16) (u16)
|
||||
|
||||
#define tu_htonl(u32) (u32)
|
||||
#define tu_ntohl(u32) (u32)
|
||||
|
||||
#define tu_htole16(u16) (TU_BSWAP16(u16))
|
||||
#define tu_le16toh(u16) (TU_BSWAP16(u16))
|
||||
|
||||
#define tu_htole32(u32) (TU_BSWAP32(u32))
|
||||
#define tu_le32toh(u32) (TU_BSWAP32(u32))
|
||||
|
||||
#else
|
||||
#error Byte order is undefined
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_COMPILER_H_ */
|
||||
|
||||
/// @}
|
||||
169
managed_components/espressif__tinyusb/src/common/tusb_debug.h
Normal file
169
managed_components/espressif__tinyusb/src/common/tusb_debug.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022, 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_DEBUG_H_
|
||||
#define _TUSB_DEBUG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// CFG_TUSB_DEBUG for debugging
|
||||
// 0 : no debug
|
||||
// 1 : print error
|
||||
// 2 : print warning
|
||||
// 3 : print info
|
||||
#if CFG_TUSB_DEBUG
|
||||
|
||||
// Enum to String for debugging purposes
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
extern char const* const tu_str_speed[];
|
||||
extern char const* const tu_str_std_request[];
|
||||
extern char const* const tu_str_xfer_result[];
|
||||
#endif
|
||||
|
||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
|
||||
|
||||
#ifdef CFG_TUSB_DEBUG_PRINTF
|
||||
extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
|
||||
#define tu_printf CFG_TUSB_DEBUG_PRINTF
|
||||
#else
|
||||
#define tu_printf printf
|
||||
#endif
|
||||
|
||||
static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) {
|
||||
for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
|
||||
tu_printf("\r\n");
|
||||
}
|
||||
|
||||
// Log with Level
|
||||
#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
|
||||
#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
|
||||
#define TU_LOG_BUF(n, ...) TU_XSTRCAT3(TU_LOG, n, _BUF)(__VA_ARGS__)
|
||||
#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
|
||||
#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
|
||||
#define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
#define TU_LOG_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
|
||||
// Log Level 1: Error
|
||||
#define TU_LOG1 tu_printf
|
||||
#define TU_LOG1_MEM tu_print_mem
|
||||
#define TU_LOG1_BUF(_x, _n) tu_print_buf((uint8_t const*)(_x), _n)
|
||||
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
|
||||
#define TU_LOG1_HEX(_x) tu_printf(#_x " = 0x%lX\r\n", (unsigned long) (_x) )
|
||||
|
||||
// Log Level 2: Warn
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
#define TU_LOG2 TU_LOG1
|
||||
#define TU_LOG2_MEM TU_LOG1_MEM
|
||||
#define TU_LOG2_BUF TU_LOG1_BUF
|
||||
#define TU_LOG2_INT TU_LOG1_INT
|
||||
#define TU_LOG2_HEX TU_LOG1_HEX
|
||||
#endif
|
||||
|
||||
// Log Level 3: Info
|
||||
#if CFG_TUSB_DEBUG >= 3
|
||||
#define TU_LOG3 TU_LOG1
|
||||
#define TU_LOG3_MEM TU_LOG1_MEM
|
||||
#define TU_LOG3_BUF TU_LOG1_BUF
|
||||
#define TU_LOG3_INT TU_LOG1_INT
|
||||
#define TU_LOG3_HEX TU_LOG1_HEX
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t key;
|
||||
const char* data;
|
||||
} tu_lookup_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t count;
|
||||
tu_lookup_entry_t const* items;
|
||||
} tu_lookup_table_t;
|
||||
|
||||
static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) {
|
||||
for(uint16_t i=0; i<p_table->count; i++) {
|
||||
if (p_table->items[i].key == key) { return p_table->items[i].data; }
|
||||
}
|
||||
|
||||
// not found return the key value in hex
|
||||
static char not_found[11];
|
||||
snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key);
|
||||
return not_found;
|
||||
}
|
||||
|
||||
#endif // CFG_TUSB_DEBUG
|
||||
|
||||
#ifndef TU_LOG
|
||||
#define TU_LOG(n, ...)
|
||||
#define TU_LOG_MEM(n, ...)
|
||||
#define TU_LOG_BUF(n, ...)
|
||||
#define TU_LOG_INT(n, ...)
|
||||
#define TU_LOG_HEX(n, ...)
|
||||
#define TU_LOG_LOCATION()
|
||||
#define TU_LOG_FAILED()
|
||||
#endif
|
||||
|
||||
// TODO replace all TU_LOGn with TU_LOG(n)
|
||||
|
||||
#define TU_LOG0(...)
|
||||
#define TU_LOG0_MEM(...)
|
||||
#define TU_LOG0_BUF(...)
|
||||
#define TU_LOG0_INT(...)
|
||||
#define TU_LOG0_HEX(...)
|
||||
|
||||
#ifndef TU_LOG1
|
||||
#define TU_LOG1(...)
|
||||
#define TU_LOG1_MEM(...)
|
||||
#define TU_LOG1_BUF(...)
|
||||
#define TU_LOG1_INT(...)
|
||||
#define TU_LOG1_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifndef TU_LOG2
|
||||
#define TU_LOG2(...)
|
||||
#define TU_LOG2_MEM(...)
|
||||
#define TU_LOG2_BUF(...)
|
||||
#define TU_LOG2_INT(...)
|
||||
#define TU_LOG2_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifndef TU_LOG3
|
||||
#define TU_LOG3(...)
|
||||
#define TU_LOG3_MEM(...)
|
||||
#define TU_LOG3_BUF(...)
|
||||
#define TU_LOG3_INT(...)
|
||||
#define TU_LOG3_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_DEBUG_H_ */
|
||||
1094
managed_components/espressif__tinyusb/src/common/tusb_fifo.c
Normal file
1094
managed_components/espressif__tinyusb/src/common/tusb_fifo.c
Normal file
File diff suppressed because it is too large
Load Diff
199
managed_components/espressif__tinyusb/src/common/tusb_fifo.h
Normal file
199
managed_components/espressif__tinyusb/src/common/tusb_fifo.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* 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_FIFO_H_
|
||||
#define _TUSB_FIFO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Due to the use of unmasked pointers, this FIFO does not suffer from losing
|
||||
// one item slice. Furthermore, write and read operations are completely
|
||||
// decoupled as write and read functions do not modify a common state. Henceforth,
|
||||
// writing or reading from the FIFO within an ISR is safe as long as no other
|
||||
// process (thread or ISR) interferes.
|
||||
// Also, this FIFO is ready to be used in combination with a DMA as the write and
|
||||
// read pointers can be updated from within a DMA ISR. Overflows are detectable
|
||||
// within a certain number (see tu_fifo_overflow()).
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "osal/osal.h"
|
||||
|
||||
// mutex is only needed for RTOS
|
||||
// for OS None, we don't get preempted
|
||||
#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED
|
||||
|
||||
/* Write/Read index is always in the range of:
|
||||
* 0 .. 2*depth-1
|
||||
* The extra window allow us to determine the fifo state of empty or full with only 2 indices
|
||||
* Following are examples with depth = 3
|
||||
*
|
||||
* - empty: W = R
|
||||
* |
|
||||
* -------------------------
|
||||
* | 0 | RW| 2 | 3 | 4 | 5 |
|
||||
*
|
||||
* - full 1: W > R
|
||||
* |
|
||||
* -------------------------
|
||||
* | 0 | R | 2 | 3 | W | 5 |
|
||||
*
|
||||
* - full 2: W < R
|
||||
* |
|
||||
* -------------------------
|
||||
* | 0 | 1 | W | 3 | 4 | R |
|
||||
*
|
||||
* - Number of items in the fifo can be determined in either cases:
|
||||
* - case W >= R: Count = W - R
|
||||
* - case W < R: Count = 2*depth - (R - W)
|
||||
*
|
||||
* In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth.
|
||||
* However, in over-writable mode, write index can be repeatedly increased and count can be
|
||||
* temporarily larger than depth (overflowed condition) e.g
|
||||
*
|
||||
* - Overflowed 1: write(3), write(1)
|
||||
* In this case we will adjust Read index when read()/peek() is called so that count = depth.
|
||||
* |
|
||||
* -------------------------
|
||||
* | R | 1 | 2 | 3 | W | 5 |
|
||||
*
|
||||
* - Double Overflowed i.e index is out of allowed range [0,2*depth)
|
||||
* This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g:
|
||||
* write(3), write(1), write(2)
|
||||
* This must be prevented since it will cause unrecoverable state, in above example
|
||||
* if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify
|
||||
* read index in write() function, which cause race condition. We will re-position write index so that
|
||||
* after data is written it is a full fifo i.e W = depth - R
|
||||
*
|
||||
* re-position W = 1 before write(2)
|
||||
* Note: we should also move data from mem[3] to read index as well, but deliberately skipped here
|
||||
* since it is an expensive operation !!!
|
||||
* |
|
||||
* -------------------------
|
||||
* | R | W | 2 | 3 | 4 | 5 |
|
||||
*
|
||||
* perform write(2), result is still a full fifo.
|
||||
*
|
||||
* |
|
||||
* -------------------------
|
||||
* | R | 1 | 2 | W | 4 | 5 |
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t* buffer ; // buffer pointer
|
||||
uint16_t depth ; // max items
|
||||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t item_size : 15; // size of each item
|
||||
bool overwritable : 1 ; // ovwerwritable when full
|
||||
};
|
||||
|
||||
volatile uint16_t wr_idx ; // write index
|
||||
volatile uint16_t rd_idx ; // read index
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_wr;
|
||||
osal_mutex_t mutex_rd;
|
||||
#endif
|
||||
|
||||
} tu_fifo_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t len_lin ; ///< linear length in item size
|
||||
uint16_t len_wrap ; ///< wrapped length in item size
|
||||
void * ptr_lin ; ///< linear part start pointer
|
||||
void * ptr_wrap ; ///< wrapped part start pointer
|
||||
} tu_fifo_buffer_info_t;
|
||||
|
||||
#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable){\
|
||||
.buffer = _buffer, \
|
||||
.depth = _depth, \
|
||||
.item_size = sizeof(_type), \
|
||||
.overwritable = _overwritable, \
|
||||
}
|
||||
|
||||
#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
|
||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
|
||||
|
||||
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
|
||||
bool tu_fifo_clear(tu_fifo_t *f);
|
||||
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
|
||||
f->mutex_wr = wr_mutex;
|
||||
f->mutex_rd = rd_mutex;
|
||||
}
|
||||
#else
|
||||
#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
|
||||
#endif
|
||||
|
||||
bool tu_fifo_write (tu_fifo_t* f, void const * data);
|
||||
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * data, uint16_t n);
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
|
||||
#endif
|
||||
|
||||
bool tu_fifo_read (tu_fifo_t* f, void * buffer);
|
||||
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t n);
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
|
||||
#endif
|
||||
|
||||
bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
|
||||
uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
|
||||
|
||||
uint16_t tu_fifo_count (tu_fifo_t* f);
|
||||
uint16_t tu_fifo_remaining (tu_fifo_t* f);
|
||||
bool tu_fifo_empty (tu_fifo_t* f);
|
||||
bool tu_fifo_full (tu_fifo_t* f);
|
||||
bool tu_fifo_overflowed (tu_fifo_t* f);
|
||||
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
uint16_t tu_fifo_depth(tu_fifo_t* f) {
|
||||
return f->depth;
|
||||
}
|
||||
|
||||
// Pointer modifications intended to be used in combinations with DMAs.
|
||||
// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
|
||||
void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n);
|
||||
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
|
||||
|
||||
// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies
|
||||
// to handle a possible wrapping part. These functions deliver a pointer to start
|
||||
// reading/writing from/to and a valid linear length along which no wrap occurs.
|
||||
void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info);
|
||||
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_FIFO_H_ */
|
||||
669
managed_components/espressif__tinyusb/src/common/tusb_mcu.h
Normal file
669
managed_components/espressif__tinyusb/src/common/tusb_mcu.h
Normal file
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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_MCU_H_
|
||||
#define TUSB_MCU_H_
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Port/Platform Specific
|
||||
// TUP stand for TinyUSB Port/Platform (can be renamed)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Unaligned Memory Access -------------//
|
||||
|
||||
#ifdef __ARM_ARCH
|
||||
// ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access
|
||||
#if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1
|
||||
#define TUP_ARCH_STRICT_ALIGN 0
|
||||
#else
|
||||
#define TUP_ARCH_STRICT_ALIGN 1
|
||||
#endif
|
||||
#else
|
||||
// TODO default to strict align for others
|
||||
// Should investigate other architecture such as risv, xtensa, mips for optimal setting
|
||||
#define TUP_ARCH_STRICT_ALIGN 1
|
||||
#endif
|
||||
|
||||
/* USB Controller Attributes for Device, Host or MCU (both)
|
||||
* - ENDPOINT_MAX: max (logical) number of endpoint
|
||||
* - ENDPOINT_EXCLUSIVE_NUMBER: endpoint number with different direction IN and OUT aren't allowed,
|
||||
* e.g EP1 OUT & EP1 IN cannot exist together
|
||||
* - RHPORT_HIGHSPEED: support highspeed with on-chip PHY
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// NXP
|
||||
//--------------------------------------------------------------------+
|
||||
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 5
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_USBIP_OHCI
|
||||
#define TUP_OHCI_RHPORTS 2
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 5
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC54)
|
||||
// TODO USB0 has 5, USB1 has 6
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC55)
|
||||
// TODO USB0 has 5, USB1 has 6
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
// USB0 has 6 with HS PHY, USB1 has 4 only FS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
|
||||
// USB0 is chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_MCX
|
||||
|
||||
// USB1 is chipidea HS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXA15)
|
||||
// USB0 is chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_MCX
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX)
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#if __CORTEX_M == 7
|
||||
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 1
|
||||
#define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 1
|
||||
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K)
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_KINETIS
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MM32F327X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Nordic
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
|
||||
// 8 CBI + 1 ISO
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Microchip
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \
|
||||
TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMG)
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMX7X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 10
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \
|
||||
TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ST
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F1)
|
||||
// - F102, F103 use fsdev
|
||||
// - F105, F107 use dwc2
|
||||
#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
|
||||
defined (STM32F107xB) || defined (STM32F107xC)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
#define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
#elif defined(STM32F102x6) || defined(STM32F102xB) || \
|
||||
defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#else
|
||||
#error "Unsupported STM32F1 mcu"
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F2)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// FS has 4 ep, HS has 5 ep
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F3)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F7)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// FS has 6, HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
// MCU with on-chip HS Phy
|
||||
#if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx)
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS
|
||||
#endif
|
||||
|
||||
// Enable dcache if DMA is enabled
|
||||
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE
|
||||
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H7)
|
||||
#include "stm32h7xx.h"
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
#if __CORTEX_M == 7
|
||||
// Enable dcache if DMA is enabled
|
||||
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE
|
||||
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H5)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32G4)
|
||||
// Device controller
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
|
||||
// TypeC controller
|
||||
#define TUP_USBIP_TYPEC_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_TYPEC_RHPORTS_NUM 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32G0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32C0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L4)
|
||||
// - L4x2, L4x3 use fsdev
|
||||
// - L4x4, L4x6, L4x7, L4x9 use dwc2
|
||||
#if defined (STM32L475xx) || defined (STM32L476xx) || \
|
||||
defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
|
||||
defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \
|
||||
defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
|
||||
defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \
|
||||
defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#else
|
||||
#error "Unsupported STM32L4 mcu"
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32WB)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32WBA)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32U5)
|
||||
#if defined (STM32U535xx) || defined (STM32U545xx)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#else
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
|
||||
#if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
|
||||
defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#else
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L5)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32U0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H7RS, OPT_MCU_STM32N6)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// FS has 6, HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
// MCU with on-chip HS Phy
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
// Enable dcache if DMA is enabled
|
||||
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE
|
||||
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Sony
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CXD56)
|
||||
#define TUP_DCD_ENDPOINT_MAX 7
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TI
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
|
||||
#define TUP_USBIP_MUSB
|
||||
#define TUP_USBIP_MUSB_TI
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ValentyUSB (Litex)
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Nuvoton
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC120)
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC505)
|
||||
#define TUP_DCD_ENDPOINT_MAX 12
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Espressif
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_ESP32
|
||||
#define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
|
||||
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_ESP32S3
|
||||
#define TUP_MCU_MULTIPLE_CORE 1
|
||||
#endif
|
||||
|
||||
// Disable slave if DMA is enabled
|
||||
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_ESP32
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS
|
||||
#define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep
|
||||
|
||||
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
|
||||
|
||||
#define TUP_MCU_MULTIPLE_CORE 1
|
||||
|
||||
// Disable slave if DMA is enabled
|
||||
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
|
||||
|
||||
// Enable dcache if DMA is enabled
|
||||
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
|
||||
#define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE
|
||||
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C5, OPT_MCU_ESP32C6, OPT_MCU_ESP32C61, OPT_MCU_ESP32H2)
|
||||
#if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
|
||||
#error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 0
|
||||
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Dialog
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_DA1469X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Raspberry Pi
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
|
||||
#define TUP_DCD_EDPT_ISO_ALLOC
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_MCU_MULTIPLE_CORE 1
|
||||
|
||||
#define TU_ATTR_FAST_FUNC __not_in_flash("tinyusb")
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Silabs
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 7
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Renesas
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX)
|
||||
#define TUP_USBIP_RUSB2
|
||||
#define TUP_DCD_ENDPOINT_MAX 10
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// GigaDevice
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Broadcom
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Infineon
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BridgeTek
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_FT90X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_FT93X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Allwinner
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_F1C100S)
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// WCH
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBHS)
|
||||
#define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V103)
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 1
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
|
||||
// v20x support both port0 FSDEV (USBD) and port1 USBFS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#ifndef CFG_TUH_WCH_USBIP_USBFS
|
||||
#define CFG_TUH_WCH_USBIP_USBFS 1
|
||||
#endif
|
||||
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_CH32
|
||||
|
||||
// default to FSDEV for device
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_FSDEV)
|
||||
#define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
|
||||
// v307 support both FS and HS, default to HS
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBHS)
|
||||
#define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Analog Devices
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
|
||||
#define TUP_USBIP_MUSB
|
||||
#define TUP_USBIP_MUSB_ADI
|
||||
#define TUP_DCD_ENDPOINT_MAX 12
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ArteryTek
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F403A_407)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F413)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F415)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F435_437)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F423)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F402_405)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
// AT32F405xx has on-chip HS PHY
|
||||
#if defined(AT32F405CBT7) || defined(AT32F405CBU7) || \
|
||||
defined(AT32F405CCT7) || defined(AT32F405CCU7) || \
|
||||
defined(AT32F405KBU7_4) || defined(AT32F405KCU7_4) || \
|
||||
defined(AT32F405RBT7_7) || defined(AT32F405RBT7) || \
|
||||
defined(AT32F405RCT7_7) || defined(AT32F405RCT7)
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_AT32F425)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_AT32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// External USB controller
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
|
||||
#ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL
|
||||
#define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Default Values
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef TUP_MCU_MULTIPLE_CORE
|
||||
#define TUP_MCU_MULTIPLE_CORE 0
|
||||
#endif
|
||||
|
||||
#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
|
||||
#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#endif
|
||||
|
||||
// Default to fullspeed if not defined
|
||||
#ifndef TUP_RHPORT_HIGHSPEED
|
||||
#define TUP_RHPORT_HIGHSPEED 0
|
||||
#endif
|
||||
|
||||
// fast function, normally mean placing function in SRAM
|
||||
#ifndef TU_ATTR_FAST_FUNC
|
||||
#define TU_ATTR_FAST_FUNC
|
||||
#endif
|
||||
|
||||
// USBIP that support ISO alloc & activate API
|
||||
#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) || defined(TUP_USBIP_MUSB)
|
||||
#define TUP_DCD_EDPT_ISO_ALLOC
|
||||
#endif
|
||||
|
||||
#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA_ENABLE == 0
|
||||
#define TUP_MEM_CONST_ADDR
|
||||
#endif
|
||||
|
||||
#endif
|
||||
172
managed_components/espressif__tinyusb/src/common/tusb_private.h
Normal file
172
managed_components/espressif__tinyusb/src/common/tusb_private.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022, 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_PRIVATE_H_
|
||||
#define TUSB_PRIVATE_H_
|
||||
|
||||
// Internal Helper used by Host and Device Stack
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TUP_USBIP_CONTROLLER_NUM 2
|
||||
extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM];
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
volatile uint8_t busy : 1;
|
||||
volatile uint8_t stalled : 1;
|
||||
volatile uint8_t claimed : 1;
|
||||
}tu_edpt_state_t;
|
||||
|
||||
typedef struct {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t is_host : 1; // 1: host, 0: device
|
||||
uint8_t is_mps512 : 1; // 1: 512, 0: 64 since stream is used for Bulk only
|
||||
};
|
||||
uint8_t ep_addr;
|
||||
uint16_t ep_bufsize;
|
||||
|
||||
uint8_t* ep_buf; // TODO xfer_fifo can skip this buffer
|
||||
tu_fifo_t ff;
|
||||
|
||||
// mutex: read if rx, otherwise write
|
||||
OSAL_MUTEX_DEF(ff_mutexdef);
|
||||
|
||||
}tu_edpt_stream_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if endpoint descriptor is valid per USB specs
|
||||
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed, bool is_host);
|
||||
|
||||
// Bind all endpoint of a interface descriptor to class driver
|
||||
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
|
||||
|
||||
// Calculate total length of n interfaces (depending on IAD)
|
||||
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len);
|
||||
|
||||
// Claim an endpoint with provided mutex
|
||||
bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
|
||||
|
||||
// Release an endpoint with provided mutex
|
||||
bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint Stream
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Init an endpoint stream
|
||||
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
|
||||
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize);
|
||||
|
||||
// Deinit an endpoint stream
|
||||
bool tu_edpt_stream_deinit(tu_edpt_stream_t* s);
|
||||
|
||||
// Open an stream for an endpoint
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_open(tu_edpt_stream_t* s, tusb_desc_endpoint_t const *desc_ep) {
|
||||
tu_fifo_clear(&s->ff);
|
||||
s->ep_addr = desc_ep->bEndpointAddress;
|
||||
s->is_mps512 = (tu_edpt_packet_size(desc_ep) == 512) ? 1 : 0;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_close(tu_edpt_stream_t* s) {
|
||||
s->ep_addr = 0;
|
||||
}
|
||||
|
||||
// Clear fifo
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
|
||||
return tu_fifo_clear(&s->ff);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream Write
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Write to stream
|
||||
uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize);
|
||||
|
||||
// Start an usb transfer if endpoint is not busy
|
||||
uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s);
|
||||
|
||||
// Start an zero-length packet if needed
|
||||
bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes);
|
||||
|
||||
// Get the number of bytes available for writing to FIFO
|
||||
// Note: if no fifo, return endpoint size if not busy, 0 otherwise
|
||||
uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream Read
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Read from stream
|
||||
uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize);
|
||||
|
||||
// Start an usb transfer if endpoint is not busy
|
||||
uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s);
|
||||
|
||||
// Complete read transfer by writing EP -> FIFO. Must be called in the transfer complete callback
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) {
|
||||
if (tu_fifo_depth(&s->ff)) {
|
||||
tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Complete read transfer with provided buffer
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_read_xfer_complete_with_buf(tu_edpt_stream_t* s, const void * buf, uint32_t xferred_bytes) {
|
||||
if (tu_fifo_depth(&s->ff)) {
|
||||
tu_fifo_write_n(&s->ff, buf, (uint16_t) xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of bytes available for reading
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) {
|
||||
return (uint32_t) tu_fifo_count(&s->ff);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) {
|
||||
return tu_fifo_peek(&s->ff, ch);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
569
managed_components/espressif__tinyusb/src/common/tusb_types.h
Normal file
569
managed_components/espressif__tinyusb/src/common/tusb_types.h
Normal file
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_TYPES_H_
|
||||
#define TUSB_TYPES_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "tusb_compiler.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------- Device DCache declaration -------------//
|
||||
#define TUD_EPBUF_DCACHE_SIZE(_size) (CFG_TUD_MEM_DCACHE_ENABLE ? \
|
||||
(TU_DIV_CEIL(_size, CFG_TUD_MEM_DCACHE_LINE_SIZE) * CFG_TUD_MEM_DCACHE_LINE_SIZE) : (_size))
|
||||
|
||||
// Declare an endpoint buffer with uint8_t[size]
|
||||
#define TUD_EPBUF_DEF(_name, _size) \
|
||||
union { \
|
||||
CFG_TUD_MEM_ALIGN uint8_t _name[_size]; \
|
||||
TU_ATTR_ALIGNED(CFG_TUD_MEM_DCACHE_ENABLE ? CFG_TUD_MEM_DCACHE_LINE_SIZE : 1) uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(_size)]; \
|
||||
}
|
||||
|
||||
// Declare an endpoint buffer with a type
|
||||
#define TUD_EPBUF_TYPE_DEF(_type, _name) \
|
||||
union { \
|
||||
CFG_TUD_MEM_ALIGN _type _name; \
|
||||
TU_ATTR_ALIGNED(CFG_TUD_MEM_DCACHE_ENABLE ? CFG_TUD_MEM_DCACHE_LINE_SIZE : 1) uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
|
||||
}
|
||||
|
||||
//------------- Host DCache declaration -------------//
|
||||
#define TUH_EPBUF_DCACHE_SIZE(_size) (CFG_TUH_MEM_DCACHE_ENABLE ? \
|
||||
(TU_DIV_CEIL(_size, CFG_TUH_MEM_DCACHE_LINE_SIZE) * CFG_TUH_MEM_DCACHE_LINE_SIZE) : (_size))
|
||||
|
||||
// Declare an endpoint buffer with uint8_t[size]
|
||||
#define TUH_EPBUF_DEF(_name, _size) \
|
||||
union { \
|
||||
CFG_TUH_MEM_ALIGN uint8_t _name[_size]; \
|
||||
TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 1) uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(_size)]; \
|
||||
}
|
||||
|
||||
// Declare an endpoint buffer with a type
|
||||
#define TUH_EPBUF_TYPE_DEF(_type, _name) \
|
||||
union { \
|
||||
CFG_TUH_MEM_ALIGN _type _name; \
|
||||
TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 1) uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* CONSTANTS
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
typedef enum {
|
||||
TUSB_ROLE_INVALID = 0,
|
||||
TUSB_ROLE_DEVICE = 0x1,
|
||||
TUSB_ROLE_HOST = 0x2,
|
||||
} tusb_role_t;
|
||||
|
||||
/// defined base on EHCI specs value for Endpoint Speed
|
||||
typedef enum {
|
||||
TUSB_SPEED_FULL = 0,
|
||||
TUSB_SPEED_LOW = 1,
|
||||
TUSB_SPEED_HIGH = 2,
|
||||
TUSB_SPEED_AUTO = 0xaa,
|
||||
TUSB_SPEED_INVALID = 0xff,
|
||||
} tusb_speed_t;
|
||||
|
||||
/// defined base on USB Specs Endpoint's bmAttributes
|
||||
typedef enum {
|
||||
TUSB_XFER_CONTROL = 0,
|
||||
TUSB_XFER_ISOCHRONOUS = 1,
|
||||
TUSB_XFER_BULK = 2,
|
||||
TUSB_XFER_INTERRUPT = 3
|
||||
} tusb_xfer_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_DIR_OUT = 0,
|
||||
TUSB_DIR_IN = 1,
|
||||
|
||||
TUSB_DIR_IN_MASK = 0x80
|
||||
} tusb_dir_t;
|
||||
|
||||
enum {
|
||||
TUSB_EPSIZE_BULK_FS = 64,
|
||||
TUSB_EPSIZE_BULK_HS = 512,
|
||||
|
||||
TUSB_EPSIZE_ISO_FS_MAX = 1023,
|
||||
TUSB_EPSIZE_ISO_HS_MAX = 1024,
|
||||
};
|
||||
|
||||
/// Isochronous Endpoint 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;
|
||||
|
||||
/// USB Descriptor Types
|
||||
typedef enum {
|
||||
TUSB_DESC_DEVICE = 0x01,
|
||||
TUSB_DESC_CONFIGURATION = 0x02,
|
||||
TUSB_DESC_STRING = 0x03,
|
||||
TUSB_DESC_INTERFACE = 0x04,
|
||||
TUSB_DESC_ENDPOINT = 0x05,
|
||||
TUSB_DESC_DEVICE_QUALIFIER = 0x06,
|
||||
TUSB_DESC_OTHER_SPEED_CONFIG = 0x07,
|
||||
TUSB_DESC_INTERFACE_POWER = 0x08,
|
||||
TUSB_DESC_OTG = 0x09,
|
||||
TUSB_DESC_DEBUG = 0x0A,
|
||||
TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B,
|
||||
|
||||
TUSB_DESC_BOS = 0x0F,
|
||||
TUSB_DESC_DEVICE_CAPABILITY = 0x10,
|
||||
|
||||
TUSB_DESC_FUNCTIONAL = 0x21,
|
||||
|
||||
// Class Specific Descriptor
|
||||
TUSB_DESC_CS_DEVICE = 0x21,
|
||||
TUSB_DESC_CS_CONFIGURATION = 0x22,
|
||||
TUSB_DESC_CS_STRING = 0x23,
|
||||
TUSB_DESC_CS_INTERFACE = 0x24,
|
||||
TUSB_DESC_CS_ENDPOINT = 0x25,
|
||||
|
||||
TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
|
||||
TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
|
||||
} tusb_desc_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_GET_STATUS = 0 ,
|
||||
TUSB_REQ_CLEAR_FEATURE = 1 ,
|
||||
TUSB_REQ_RESERVED = 2 ,
|
||||
TUSB_REQ_SET_FEATURE = 3 ,
|
||||
TUSB_REQ_RESERVED2 = 4 ,
|
||||
TUSB_REQ_SET_ADDRESS = 5 ,
|
||||
TUSB_REQ_GET_DESCRIPTOR = 6 ,
|
||||
TUSB_REQ_SET_DESCRIPTOR = 7 ,
|
||||
TUSB_REQ_GET_CONFIGURATION = 8 ,
|
||||
TUSB_REQ_SET_CONFIGURATION = 9 ,
|
||||
TUSB_REQ_GET_INTERFACE = 10 ,
|
||||
TUSB_REQ_SET_INTERFACE = 11 ,
|
||||
TUSB_REQ_SYNCH_FRAME = 12
|
||||
} tusb_request_code_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_FEATURE_EDPT_HALT = 0,
|
||||
TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
|
||||
TUSB_REQ_FEATURE_TEST_MODE = 2
|
||||
} tusb_request_feature_selector_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_TYPE_STANDARD = 0,
|
||||
TUSB_REQ_TYPE_CLASS,
|
||||
TUSB_REQ_TYPE_VENDOR,
|
||||
TUSB_REQ_TYPE_INVALID
|
||||
} tusb_request_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_RCPT_DEVICE =0,
|
||||
TUSB_REQ_RCPT_INTERFACE,
|
||||
TUSB_REQ_RCPT_ENDPOINT,
|
||||
TUSB_REQ_RCPT_OTHER
|
||||
} tusb_request_recipient_t;
|
||||
|
||||
// https://www.usb.org/defined-class-codes
|
||||
typedef enum {
|
||||
TUSB_CLASS_UNSPECIFIED = 0 ,
|
||||
TUSB_CLASS_AUDIO = 1 ,
|
||||
TUSB_CLASS_CDC = 2 ,
|
||||
TUSB_CLASS_HID = 3 ,
|
||||
TUSB_CLASS_RESERVED_4 = 4 ,
|
||||
TUSB_CLASS_PHYSICAL = 5 ,
|
||||
TUSB_CLASS_IMAGE = 6 ,
|
||||
TUSB_CLASS_PRINTER = 7 ,
|
||||
TUSB_CLASS_MSC = 8 ,
|
||||
TUSB_CLASS_HUB = 9 ,
|
||||
TUSB_CLASS_CDC_DATA = 10 ,
|
||||
TUSB_CLASS_SMART_CARD = 11 ,
|
||||
TUSB_CLASS_RESERVED_12 = 12 ,
|
||||
TUSB_CLASS_CONTENT_SECURITY = 13 ,
|
||||
TUSB_CLASS_VIDEO = 14 ,
|
||||
TUSB_CLASS_PERSONAL_HEALTHCARE = 15 ,
|
||||
TUSB_CLASS_AUDIO_VIDEO = 16 ,
|
||||
|
||||
TUSB_CLASS_DIAGNOSTIC = 0xDC ,
|
||||
TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 ,
|
||||
TUSB_CLASS_MISC = 0xEF ,
|
||||
TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
|
||||
TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
} tusb_class_code_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MISC_SUBCLASS_COMMON = 2
|
||||
}misc_subclass_type_t;
|
||||
|
||||
typedef enum {
|
||||
MISC_PROTOCOL_IAD = 1
|
||||
} misc_protocol_type_t;
|
||||
|
||||
typedef enum {
|
||||
APP_SUBCLASS_USBTMC = 0x03,
|
||||
APP_SUBCLASS_DFU_RUNTIME = 0x01
|
||||
} app_subclass_type_t;
|
||||
|
||||
typedef enum {
|
||||
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
|
||||
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
|
||||
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
|
||||
DEVICE_CAPABILITY_CONTAINER_id = 0x04,
|
||||
DEVICE_CAPABILITY_PLATFORM = 0x05,
|
||||
DEVICE_CAPABILITY_POWER_DELIVERY = 0x06,
|
||||
DEVICE_CAPABILITY_BATTERY_INFO = 0x07,
|
||||
DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08,
|
||||
DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09,
|
||||
DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A,
|
||||
DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B,
|
||||
DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C,
|
||||
DEVICE_CAPABILITY_BILLBOARD = 0x0D,
|
||||
DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
|
||||
DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
|
||||
DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
|
||||
} device_capability_type_t;
|
||||
|
||||
enum {
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
|
||||
TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
|
||||
};
|
||||
|
||||
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
|
||||
|
||||
// USB 2.0 Spec Table 9-7: Test Mode Selectors
|
||||
typedef enum {
|
||||
TUSB_FEATURE_TEST_J = 1,
|
||||
TUSB_FEATURE_TEST_K = 2,
|
||||
TUSB_FEATURE_TEST_SE0_NAK = 3,
|
||||
TUSB_FEATURE_TEST_PACKET = 4,
|
||||
TUSB_FEATURE_TEST_FORCE_ENABLE = 5,
|
||||
} tusb_feature_test_mode_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
typedef enum {
|
||||
XFER_RESULT_SUCCESS = 0,
|
||||
XFER_RESULT_FAILED,
|
||||
XFER_RESULT_STALLED,
|
||||
XFER_RESULT_TIMEOUT,
|
||||
XFER_RESULT_INVALID
|
||||
} xfer_result_t;
|
||||
|
||||
#define tusb_xfer_result_t xfer_result_t
|
||||
|
||||
// TODO remove
|
||||
enum {
|
||||
DESC_OFFSET_LEN = 0,
|
||||
DESC_OFFSET_TYPE = 1,
|
||||
DESC_OFFSET_SUBTYPE = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
INTERFACE_INVALID_NUMBER = 0xff
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
|
||||
MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
|
||||
MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
|
||||
MS_OS_20_FEATURE_COMPATBLE_ID = 0x03,
|
||||
MS_OS_20_FEATURE_REG_PROPERTY = 0x04,
|
||||
MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05,
|
||||
MS_OS_20_FEATURE_MODEL_ID = 0x06,
|
||||
MS_OS_20_FEATURE_CCGP_DEVICE = 0x07,
|
||||
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
|
||||
} microsoft_os_20_type_t;
|
||||
|
||||
enum {
|
||||
CONTROL_STAGE_IDLE = 0,
|
||||
CONTROL_STAGE_SETUP, // 1
|
||||
CONTROL_STAGE_DATA, // 2
|
||||
CONTROL_STAGE_ACK // 3
|
||||
};
|
||||
|
||||
enum {
|
||||
TUSB_INDEX_INVALID_8 = 0xFFu
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
tusb_role_t role;
|
||||
tusb_speed_t speed;
|
||||
} tusb_rhport_init_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USB Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Start of all packed definitions for compiler without per-type packed
|
||||
TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
|
||||
/// USB Device Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
|
||||
uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
|
||||
|
||||
uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF).
|
||||
uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF).
|
||||
uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF).
|
||||
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
|
||||
|
||||
uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
|
||||
uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
|
||||
uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
|
||||
uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
|
||||
uint8_t iProduct ; ///< Index of string descriptor describing product.
|
||||
uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
|
||||
uint8_t bNumConfigurations ; ///< Number of possible configurations.
|
||||
} tusb_desc_device_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
|
||||
|
||||
// USB Binary Device Object Store (BOS) Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
|
||||
uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS
|
||||
} tusb_desc_bos_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
|
||||
|
||||
/// USB Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
|
||||
|
||||
uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration
|
||||
uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration.
|
||||
uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration
|
||||
uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one.
|
||||
uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA).
|
||||
} tusb_desc_configuration_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Interface Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
|
||||
|
||||
uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration.
|
||||
uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field
|
||||
uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe.
|
||||
uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF.
|
||||
uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
|
||||
uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface.
|
||||
uint8_t iInterface ; ///< Index of string descriptor describing this interface
|
||||
} tusb_desc_interface_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Endpoint Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; // Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; // ENDPOINT Descriptor Type
|
||||
|
||||
uint8_t bEndpointAddress ; // The address of the endpoint
|
||||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
|
||||
uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
|
||||
uint8_t usage : 2; // Data, Feedback, Implicit feedback
|
||||
uint8_t : 2;
|
||||
} bmAttributes;
|
||||
|
||||
uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame
|
||||
uint8_t bInterval ; // Polling interval, in frames or microframes depending on the operating speed
|
||||
} tusb_desc_endpoint_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
|
||||
|
||||
/// USB Other Speed Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned
|
||||
|
||||
uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration
|
||||
uint8_t bConfigurationValue ; ///< Value to use to select configuration
|
||||
uint8_t iConfiguration ; ///< Index of string descriptor
|
||||
uint8_t bmAttributes ; ///< Same as Configuration descriptor
|
||||
uint8_t bMaxPower ; ///< Same as Configuration descriptor
|
||||
} tusb_desc_other_speed_t;
|
||||
|
||||
/// USB Device Qualifier Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Device Qualifier Type
|
||||
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
|
||||
|
||||
uint8_t bDeviceClass ; ///< Class Code
|
||||
uint8_t bDeviceSubClass ; ///< SubClass Code
|
||||
uint8_t bDeviceProtocol ; ///< Protocol Code
|
||||
|
||||
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed
|
||||
uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations
|
||||
uint8_t bReserved ; ///< Reserved for future use, must be zero
|
||||
} tusb_desc_device_qualifier_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
|
||||
|
||||
/// USB Interface Association Descriptor (IAD ECN)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
|
||||
uint8_t bFirstInterface ; ///< Index of the first associated interface.
|
||||
uint8_t bInterfaceCount ; ///< Total number of associated interfaces.
|
||||
|
||||
uint8_t bFunctionClass ; ///< Interface class ID.
|
||||
uint8_t bFunctionSubClass ; ///< Interface subclass ID.
|
||||
uint8_t bFunctionProtocol ; ///< Interface protocol ID.
|
||||
|
||||
uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
|
||||
} tusb_desc_interface_assoc_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct");
|
||||
|
||||
// USB String Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type
|
||||
uint16_t utf16le[];
|
||||
} tusb_desc_string_t;
|
||||
|
||||
// USB Binary Device Object Store (BOS)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType ;
|
||||
uint8_t bDevCapabilityType;
|
||||
uint8_t bReserved;
|
||||
uint8_t PlatformCapabilityUUID[16];
|
||||
uint8_t CapabilityData[];
|
||||
} tusb_desc_bos_platform_t;
|
||||
|
||||
// USB WebUSB URL Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bScheme;
|
||||
char url[];
|
||||
} tusb_desc_webusb_url_t;
|
||||
|
||||
// DFU Functional Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
|
||||
union {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t bitCanDnload : 1;
|
||||
uint8_t bitCanUpload : 1;
|
||||
uint8_t bitManifestationTolerant : 1;
|
||||
uint8_t bitWillDetach : 1;
|
||||
uint8_t reserved : 4;
|
||||
} bmAttributes;
|
||||
|
||||
uint8_t bAttributes;
|
||||
};
|
||||
|
||||
uint16_t wDetachTimeOut;
|
||||
uint16_t wTransferSize;
|
||||
uint16_t bcdDFUVersion;
|
||||
} tusb_desc_dfu_functional_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
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;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} tusb_control_request_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
|
||||
|
||||
TU_ATTR_PACKED_END // End of all packed definitions
|
||||
TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get direction from Endpoint address
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
|
||||
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
|
||||
}
|
||||
|
||||
// Get Endpoint number from address
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
|
||||
return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
|
||||
return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
|
||||
return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
|
||||
}
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
|
||||
tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
|
||||
return str[t];
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TUSB_TYPES_H_
|
||||
133
managed_components/espressif__tinyusb/src/common/tusb_verify.h
Normal file
133
managed_components/espressif__tinyusb/src/common/tusb_verify.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
#ifndef TUSB_VERIFY_H_
|
||||
#define TUSB_VERIFY_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "tusb_option.h"
|
||||
#include "tusb_compiler.h"
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* This file use an advanced macro technique to mimic the default parameter
|
||||
* as C++ for the sake of code simplicity. Beware of a headache macro
|
||||
* manipulation that you are told to stay away.
|
||||
*
|
||||
* This contains macros for both VERIFY and ASSERT:
|
||||
*
|
||||
* VERIFY: Used when there is an error condition which is not the
|
||||
* fault of the MCU. For example, bounds checking on data
|
||||
* sent to the micro over USB should use this function.
|
||||
* Another example is checking for buffer overflows, where
|
||||
* returning from the active function causes a NAK.
|
||||
*
|
||||
* ASSERT: Used for error conditions that are caused by MCU firmware
|
||||
* bugs. This is used to discover bugs in the code more
|
||||
* quickly. One example would be adding assertions in library
|
||||
* function calls to confirm a function's (untainted)
|
||||
* parameters are valid.
|
||||
*
|
||||
* The difference in behavior is that ASSERT triggers a breakpoint while
|
||||
* verify does not.
|
||||
*
|
||||
* #define TU_VERIFY(cond) if (!cond) return false;
|
||||
* #define TU_VERIFY(cond,ret) if (!cond) return ret;
|
||||
*
|
||||
* #define TU_ASSERT(cond) if (!cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;}
|
||||
* #define TU_ASSERT(cond,ret) if (!cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TU_VERIFY Helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
#include <stdio.h>
|
||||
#define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
|
||||
#else
|
||||
#define TU_MESS_FAILED() do {} while (0)
|
||||
#endif
|
||||
|
||||
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
|
||||
#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
|
||||
defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
|
||||
#define TU_BREAKPOINT() do { \
|
||||
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
|
||||
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
|
||||
} while(0)
|
||||
|
||||
#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF
|
||||
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
|
||||
|
||||
#elif defined(_mips)
|
||||
#define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0)
|
||||
|
||||
#else
|
||||
#define TU_BREAKPOINT() do {} while (0)
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* TU_VERIFY
|
||||
* - TU_VERIFY_1ARGS : return false if failed
|
||||
* - TU_VERIFY_2ARGS : return provided value if failed
|
||||
*------------------------------------------------------------------*/
|
||||
#define TU_VERIFY_DEFINE(_cond, _ret) \
|
||||
do { \
|
||||
if ( !(_cond) ) { return _ret; } \
|
||||
} while(0)
|
||||
|
||||
#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false)
|
||||
#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret)
|
||||
|
||||
#define TU_VERIFY(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__)
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* ASSERT
|
||||
* basically TU_VERIFY with TU_BREAKPOINT() as handler
|
||||
* - 1 arg : return false if failed
|
||||
* - 2 arg : return error if failed
|
||||
*------------------------------------------------------------------*/
|
||||
#define TU_ASSERT_DEFINE(_cond, _ret) \
|
||||
do { \
|
||||
if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
|
||||
} while(0)
|
||||
|
||||
#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false)
|
||||
#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret)
|
||||
|
||||
#ifndef TU_ASSERT
|
||||
#define TU_ASSERT(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
244
managed_components/espressif__tinyusb/src/device/dcd.h
Normal file
244
managed_components/espressif__tinyusb/src/device/dcd.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_DCD_H_
|
||||
#define TUSB_DCD_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef enum {
|
||||
DCD_EVENT_INVALID = 0, // 0
|
||||
DCD_EVENT_BUS_RESET, // 1
|
||||
DCD_EVENT_UNPLUGGED, // 2
|
||||
DCD_EVENT_SOF, // 3
|
||||
DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support
|
||||
DCD_EVENT_RESUME, // 5
|
||||
DCD_EVENT_SETUP_RECEIVED, // 6
|
||||
DCD_EVENT_XFER_COMPLETE, // 7
|
||||
USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function
|
||||
DCD_EVENT_COUNT
|
||||
} dcd_eventid_t;
|
||||
|
||||
typedef struct TU_ATTR_ALIGNED(4) {
|
||||
uint8_t rhport;
|
||||
uint8_t event_id;
|
||||
|
||||
union {
|
||||
// BUS RESET
|
||||
struct {
|
||||
tusb_speed_t speed;
|
||||
} bus_reset;
|
||||
|
||||
// SOF
|
||||
struct {
|
||||
uint32_t frame_count;
|
||||
}sof;
|
||||
|
||||
// SETUP_RECEIVED
|
||||
tusb_control_request_t setup_received;
|
||||
|
||||
// XFER_COMPLETE
|
||||
struct {
|
||||
uint8_t ep_addr;
|
||||
uint8_t result;
|
||||
uint32_t len;
|
||||
}xfer_complete;
|
||||
|
||||
// FUNC_CALL
|
||||
struct {
|
||||
void (*func) (void*);
|
||||
void* param;
|
||||
}func_call;
|
||||
};
|
||||
} dcd_event_t;
|
||||
|
||||
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Memory API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// clean/flush data cache: write cache -> memory.
|
||||
// Required before an DMA TX transfer to make sure data is in memory
|
||||
bool dcd_dcache_clean(const void* addr, uint32_t data_size);
|
||||
|
||||
// invalidate data cache: mark cache as invalid, next read will read from memory
|
||||
// Required BOTH before and after an DMA RX transfer
|
||||
bool dcd_dcache_invalidate(const void* addr, uint32_t data_size);
|
||||
|
||||
// clean and invalidate data cache
|
||||
// Required before an DMA transfer where memory is both read/write by DMA
|
||||
bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Initialize controller to device mode
|
||||
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
|
||||
|
||||
// Deinitialize controller, unset device mode.
|
||||
bool dcd_deinit(uint8_t rhport);
|
||||
|
||||
// Interrupt Handler
|
||||
void dcd_int_handler(uint8_t rhport);
|
||||
|
||||
// Enable device interrupt
|
||||
void dcd_int_enable (uint8_t rhport);
|
||||
|
||||
// Disable device interrupt
|
||||
void dcd_int_disable(uint8_t rhport);
|
||||
|
||||
// Receive Set Address request, mcu port must also include status IN response
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
|
||||
|
||||
// Wake up host
|
||||
void dcd_remote_wakeup(uint8_t rhport);
|
||||
|
||||
// Connect by enabling internal pull-up resistor on D+/D-
|
||||
void dcd_connect(uint8_t rhport);
|
||||
|
||||
// Disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(uint8_t rhport);
|
||||
|
||||
// Enable/Disable Start-of-frame interrupt. Default is disabled
|
||||
void dcd_sof_enable(uint8_t rhport, bool en);
|
||||
|
||||
#if CFG_TUD_TEST_MODE
|
||||
// Put device into a test mode (needs power cycle to quit)
|
||||
void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector);
|
||||
#endif
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when a control transfer's status stage is complete.
|
||||
// May help DCD to prepare for next control transfer, this API is optional.
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
|
||||
|
||||
// Configure endpoint's registers according to descriptor
|
||||
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
// Close all non-control endpoints, cancel all pending transfers if any.
|
||||
// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore
|
||||
// required for multiple configuration support.
|
||||
void dcd_edpt_close_all (uint8_t rhport);
|
||||
|
||||
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
||||
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
||||
// This API is optional, may be useful for register-based for transferring data.
|
||||
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
|
||||
|
||||
// Stall endpoint, any queuing transfer should be removed from endpoint
|
||||
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
// This API never calls with control endpoints, since it is auto cleared when receiving setup packet
|
||||
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
// Allocate packet buffer used by ISO endpoints
|
||||
// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
|
||||
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
|
||||
|
||||
// Configure and enable an ISO endpoint according to descriptor
|
||||
bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
#else
|
||||
// Close an endpoint.
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Event API (implemented by stack)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Called by DCD to notify device stack
|
||||
extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
|
||||
|
||||
// helper to send bus signal event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = eid;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send bus reset event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_BUS_RESET;
|
||||
event.bus_reset.speed = speed;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send setup received
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_SETUP_RECEIVED;
|
||||
memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send transfer complete event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_XFER_COMPLETE;
|
||||
event.xfer_complete.ep_addr = ep_addr;
|
||||
event.xfer_complete.len = xferred_bytes;
|
||||
event.xfer_complete.result = result;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_SOF;
|
||||
event.sof.frame_count = frame_count;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1598
managed_components/espressif__tinyusb/src/device/usbd.c
Normal file
1598
managed_components/espressif__tinyusb/src/device/usbd.c
Normal file
File diff suppressed because it is too large
Load Diff
909
managed_components/espressif__tinyusb/src/device/usbd.h
Normal file
909
managed_components/espressif__tinyusb/src/device/usbd.h
Normal file
@@ -0,0 +1,909 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_USBD_H_
|
||||
#define _TUSB_USBD_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// New API to replace tud_init() to init device stack on specific roothub port
|
||||
bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
|
||||
|
||||
// Init device stack on roothub port
|
||||
#if TUSB_VERSION_NUMBER > 2000 // 0.20.0
|
||||
TU_ATTR_DEPRECATED("Please use tusb_init(rhport, rh_init) instead")
|
||||
#endif
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tud_init (uint8_t rhport) {
|
||||
const tusb_rhport_init_t rh_init = {
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
|
||||
};
|
||||
return tud_rhport_init(rhport, &rh_init);
|
||||
}
|
||||
|
||||
// Deinit device stack on roothub port
|
||||
bool tud_deinit(uint8_t rhport);
|
||||
|
||||
// Check if device stack is already initialized
|
||||
bool tud_inited(void);
|
||||
|
||||
// Task function should be called in main/rtos loop, extended version of tud_task()
|
||||
// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever
|
||||
// - in_isr: if function is called in ISR
|
||||
void tud_task_ext(uint32_t timeout_ms, bool in_isr);
|
||||
|
||||
// Task function should be called in main/rtos loop
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tud_task (void) {
|
||||
tud_task_ext(UINT32_MAX, false);
|
||||
}
|
||||
|
||||
// Check if there is pending events need processing by tud_task()
|
||||
bool tud_task_event_ready(void);
|
||||
|
||||
#ifndef TUSB_DCD_H_
|
||||
extern void dcd_int_handler(uint8_t rhport);
|
||||
#endif
|
||||
|
||||
// Interrupt handler, name alias to DCD
|
||||
#define tud_int_handler dcd_int_handler
|
||||
|
||||
// Get current bus speed
|
||||
tusb_speed_t tud_speed_get(void);
|
||||
|
||||
// Check if device is connected (may not mounted/configured yet)
|
||||
// True if just got out of Bus Reset and received the very first data from host
|
||||
bool tud_connected(void);
|
||||
|
||||
// Check if device is connected and configured
|
||||
bool tud_mounted(void);
|
||||
|
||||
// Check if device is suspended
|
||||
bool tud_suspended(void);
|
||||
|
||||
// Check if device is ready to transfer
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tud_ready(void) {
|
||||
return tud_mounted() && !tud_suspended();
|
||||
}
|
||||
|
||||
// Remote wake up host, only if suspended and enabled by host
|
||||
bool tud_remote_wakeup(void);
|
||||
|
||||
// Enable pull-up resistor on D+ D-
|
||||
// Return false on unsupported MCUs
|
||||
bool tud_disconnect(void);
|
||||
|
||||
// Disable pull-up resistor on D+ D-
|
||||
// Return false on unsupported MCUs
|
||||
bool tud_connect(void);
|
||||
|
||||
// Enable or disable the Start Of Frame callback support
|
||||
void tud_sof_cb_enable(bool en);
|
||||
|
||||
// Carry out Data and Status stage of control transfer
|
||||
// - If len = 0, it is equivalent to sending status only
|
||||
// - If len > wLength : it will be truncated
|
||||
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len);
|
||||
|
||||
// Send STATUS (zero length) packet
|
||||
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR request
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void);
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index);
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
|
||||
// Invoked when received GET BOS DESCRIPTOR request
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_bos_cb(void);
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb(void);
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
void tud_mount_cb(void);
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void);
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en);
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void);
|
||||
|
||||
// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
|
||||
void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
|
||||
|
||||
// Invoked when a new (micro) frame started
|
||||
void tud_sof_cb(uint32_t frame_count);
|
||||
|
||||
// Invoked when received control request with VENDOR TYPE
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Device Object Store (BOS) Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_BOS_DESC_LEN 5
|
||||
|
||||
// total length, number of device caps
|
||||
#define TUD_BOS_DESCRIPTOR(_total_len, _caps_num) \
|
||||
5, TUSB_DESC_BOS, U16_TO_U8S_LE(_total_len), _caps_num
|
||||
|
||||
// Device Capability Platform 128-bit UUID + Data
|
||||
#define TUD_BOS_PLATFORM_DESCRIPTOR(...) \
|
||||
4+TU_ARGS_NUM(__VA_ARGS__), TUSB_DESC_DEVICE_CAPABILITY, DEVICE_CAPABILITY_PLATFORM, 0x00, __VA_ARGS__
|
||||
|
||||
//------------- WebUSB BOS Platform -------------//
|
||||
|
||||
// Descriptor Length
|
||||
#define TUD_BOS_WEBUSB_DESC_LEN 24
|
||||
|
||||
// Vendor Code, iLandingPage
|
||||
#define TUD_BOS_WEBUSB_DESCRIPTOR(_vendor_code, _ipage) \
|
||||
TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_WEBUSB_UUID, U16_TO_U8S_LE(0x0100), _vendor_code, _ipage)
|
||||
|
||||
#define TUD_BOS_WEBUSB_UUID \
|
||||
0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \
|
||||
0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65
|
||||
|
||||
//------------- Microsoft OS 2.0 Platform -------------//
|
||||
#define TUD_BOS_MICROSOFT_OS_DESC_LEN 28
|
||||
|
||||
// Total Length of descriptor set, vendor code
|
||||
#define TUD_BOS_MS_OS_20_DESCRIPTOR(_desc_set_len, _vendor_code) \
|
||||
TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0)
|
||||
|
||||
#define TUD_BOS_MS_OS_20_UUID \
|
||||
0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \
|
||||
0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_CONFIG_DESC_LEN (9)
|
||||
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
#define TUD_CONFIG_DESCRIPTOR(config_num, _itfcount, _stridx, _total_len, _attribute, _power_ma) \
|
||||
9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(_total_len), _itfcount, config_num, _stridx, TU_BIT(7) | _attribute, (_power_ma)/2
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 66 bytes
|
||||
#define TUD_CDC_DESC_LEN (8+9+5+5+4+5+7+9+7+7)
|
||||
|
||||
// CDC Descriptor Template
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
#define TUD_CDC_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \
|
||||
/* Interface Associate */\
|
||||
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, 0,\
|
||||
/* CDC Control Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, _stridx,\
|
||||
/* CDC Header */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
|
||||
/* CDC Call */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
|
||||
/* CDC ACM: support line request + send break */\
|
||||
4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\
|
||||
/* CDC Union */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
|
||||
/* Endpoint Notification */\
|
||||
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\
|
||||
/* CDC Data Interface */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MSC Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 23 bytes
|
||||
#define TUD_MSC_DESC_LEN (9 + 7 + 7)
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
#define TUD_MSC_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_MSC, MSC_SUBCLASS_SCSI, MSC_PROTOCOL_BOT, _stridx,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MTP Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 30 bytes
|
||||
#define TUD_MTP_DESC_LEN (9 + 7 + 7 + 7)
|
||||
|
||||
// Interface number, string index, EP event, EP event size, EP event polling, EP Out & EP In address, EP size
|
||||
#define TUD_MTP_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_polling_interval, _epout, _epin, _epsize) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUSB_CLASS_IMAGE, MTP_SUBCLASS_STILL_IMAGE, MTP_PROTOCOL_PIMA_15470, _stridx,\
|
||||
/* Endpoint Interrupt */\
|
||||
7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_polling_interval,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HID Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 25 bytes
|
||||
#define TUD_HID_DESC_LEN (9 + 9 + 7)
|
||||
|
||||
// HID Input only descriptor
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
#define TUD_HID_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epin, _epsize, _ep_interval) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\
|
||||
/* HID descriptor */\
|
||||
9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
|
||||
|
||||
// Length of template descriptor: 32 bytes
|
||||
#define TUD_HID_INOUT_DESC_LEN (9 + 9 + 7 + 7)
|
||||
|
||||
// HID Input & Output descriptor
|
||||
// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
|
||||
#define TUD_HID_INOUT_DESCRIPTOR(_itfnum, _stridx, _boot_protocol, _report_desc_len, _epout, _epin, _epsize, _ep_interval) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol) ? (uint8_t)HID_SUBCLASS_BOOT : 0), _boot_protocol, _stridx,\
|
||||
/* HID descriptor */\
|
||||
9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0111), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(_report_desc_len),\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval, \
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _ep_interval
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MIDI Descriptor Templates
|
||||
// Note: MIDI v1.0 is based on Audio v1.0
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7)
|
||||
#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \
|
||||
/* Audio Control (AC) Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\
|
||||
/* AC Header */\
|
||||
9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\
|
||||
/* MIDI Streaming (MS) Interface */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\
|
||||
/* MS Header */\
|
||||
7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN + 2 * TUD_MIDI_DESC_EP_LEN(_numcables))
|
||||
|
||||
#define TUD_MIDI_JACKID_IN_EMB(_cablenum) \
|
||||
(uint8_t)(((_cablenum) - 1) * 4 + 1)
|
||||
|
||||
#define TUD_MIDI_JACKID_IN_EXT(_cablenum) \
|
||||
(uint8_t)(((_cablenum) - 1) * 4 + 2)
|
||||
|
||||
#define TUD_MIDI_JACKID_OUT_EMB(_cablenum) \
|
||||
(uint8_t)(((_cablenum) - 1) * 4 + 3)
|
||||
|
||||
#define TUD_MIDI_JACKID_OUT_EXT(_cablenum) \
|
||||
(uint8_t)(((_cablenum) - 1) * 4 + 4)
|
||||
|
||||
#define TUD_MIDI_DESC_JACK_LEN (6 + 6 + 9 + 9)
|
||||
#define TUD_MIDI_DESC_JACK_DESC(_cablenum, _stridx) \
|
||||
/* MS In Jack (Embedded) */\
|
||||
6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_IN_EMB(_cablenum), _stridx,\
|
||||
/* MS In Jack (External) */\
|
||||
6, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_IN_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_IN_EXT(_cablenum), _stridx,\
|
||||
/* MS Out Jack (Embedded), connected to In Jack External */\
|
||||
9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, _stridx,\
|
||||
/* MS Out Jack (External), connected to In Jack Embedded */\
|
||||
9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, _stridx
|
||||
|
||||
#define TUD_MIDI_DESC_JACK(_cablenum) TUD_MIDI_DESC_JACK_DESC(_cablenum, 0)
|
||||
|
||||
#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables))
|
||||
#define TUD_MIDI_DESC_EP(_epout, _epsize, _numcables) \
|
||||
/* Endpoint: Note Audio v1.0's endpoint has 9 bytes instead of 7 */\
|
||||
9, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, 0, 0, \
|
||||
/* MS Endpoint (connected to embedded jack) */\
|
||||
(uint8_t)(4 + (_numcables)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL, _numcables
|
||||
|
||||
// Length of template descriptor (88 bytes)
|
||||
#define TUD_MIDI_DESC_LEN (TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2)
|
||||
|
||||
// MIDI simple descriptor
|
||||
// - 1 Embedded Jack In connected to 1 External Jack Out
|
||||
// - 1 Embedded Jack out connected to 1 External Jack In
|
||||
#define TUD_MIDI_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
|
||||
TUD_MIDI_DESC_HEAD(_itfnum, _stridx, 1),\
|
||||
TUD_MIDI_DESC_JACK_DESC(1, 0),\
|
||||
TUD_MIDI_DESC_EP(_epout, _epsize, 1),\
|
||||
TUD_MIDI_JACKID_IN_EMB(1),\
|
||||
TUD_MIDI_DESC_EP(_epin, _epsize, 1),\
|
||||
TUD_MIDI_JACKID_OUT_EMB(1)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Audio v2.0 Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* Standard Interface Association Descriptor (IAD) */
|
||||
#define TUD_AUDIO_DESC_IAD_LEN 8
|
||||
#define TUD_AUDIO_DESC_IAD(_firstitf, _nitfs, _stridx) \
|
||||
TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitf, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx
|
||||
|
||||
/* Standard AC Interface Descriptor(4.7.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AC_LEN 9
|
||||
#define TUD_AUDIO_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\
|
||||
TUD_AUDIO_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
|
||||
|
||||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */
|
||||
#define TUD_AUDIO_DESC_CS_AC_LEN 9
|
||||
#define TUD_AUDIO_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio_function_t, _totallen : Total number of bytes returned for the class-specific AudioControl interface i.e. Clock Source, Unit and Terminal descriptors - Do not include TUD_AUDIO_DESC_CS_AC_LEN, we already do this here*/ \
|
||||
TUD_AUDIO_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO_DESC_CS_AC_LEN), _ctrl
|
||||
|
||||
/* Clock Source Descriptor(4.7.2.1) */
|
||||
#define TUD_AUDIO_DESC_CLK_SRC_LEN 8
|
||||
#define TUD_AUDIO_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \
|
||||
TUD_AUDIO_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE, _clkid, _attr, _ctrl, _assocTerm, _stridx
|
||||
|
||||
/* Input Terminal Descriptor(4.7.2.4) */
|
||||
#define TUD_AUDIO_DESC_INPUT_TERM_LEN 17
|
||||
#define TUD_AUDIO_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \
|
||||
TUD_AUDIO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _clkid, _nchannelslogical, U32_TO_U8S_LE(_channelcfg), _idxchannelnames, U16_TO_U8S_LE(_ctrl), _stridx
|
||||
|
||||
/* Output Terminal Descriptor(4.7.2.5) */
|
||||
#define TUD_AUDIO_DESC_OUTPUT_TERM_LEN 12
|
||||
#define TUD_AUDIO_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \
|
||||
TUD_AUDIO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _clkid, U16_TO_U8S_LE(_ctrl), _stridx
|
||||
|
||||
/* Feature Unit Descriptor(4.7.2.8) */
|
||||
// 1 - Channel
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN 6+(1+1)*4
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _stridx) \
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), _stridx
|
||||
|
||||
// 2 - Channels
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4)
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx
|
||||
// 4 - Channels
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN (6+(4+1)*4)
|
||||
#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _ctrlch3, _ctrlch4, _stridx) \
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), U32_TO_U8S_LE(_ctrlch3), U32_TO_U8S_LE(_ctrlch4), _stridx
|
||||
|
||||
// For more channels, add definitions here
|
||||
|
||||
/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7
|
||||
#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \
|
||||
TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval
|
||||
|
||||
/* Standard AS Interface Descriptor(4.9.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
|
||||
#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
|
||||
TUD_AUDIO_DESC_STD_AS_INT_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
|
||||
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */
|
||||
#define TUD_AUDIO_DESC_CS_AS_INT_LEN 16
|
||||
#define TUD_AUDIO_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \
|
||||
TUD_AUDIO_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_AS_GENERAL, _termid, _ctrl, _formattype, U32_TO_U8S_LE(_formats), _nchannelsphysical, U32_TO_U8S_LE(_channelcfg), _stridx
|
||||
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */
|
||||
#define TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN 6
|
||||
#define TUD_AUDIO_DESC_TYPE_I_FORMAT(_subslotsize, _bitresolution) /* _subslotsize is number of bytes per sample (i.e. subslot) and can be 1,2,3, or 4 */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO_FORMAT_TYPE_I, _subslotsize, _bitresolution
|
||||
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN 7
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval
|
||||
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */
|
||||
#define TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN 8
|
||||
#define TUD_AUDIO_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO_CS_EP_SUBTYPE_GENERAL, _attr, _ctrl, _lockdelayunit, U16_TO_U8S_LE(_lockdelay)
|
||||
|
||||
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _epsize, _interval) \
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(_epsize), _interval
|
||||
|
||||
// AUDIO simple descriptor (UAC2) for 1 microphone input
|
||||
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
|
||||
|
||||
#define TUD_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CLK_SRC_LEN\
|
||||
+ TUD_AUDIO_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
|
||||
|
||||
#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces
|
||||
|
||||
#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
|
||||
/* Standard Interface Association Descriptor (IAD) */\
|
||||
TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
|
||||
/* Standard AC Interface Descriptor(4.7.1) */\
|
||||
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
|
||||
TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
|
||||
/* Clock Source Descriptor(4.7.2.1) */\
|
||||
TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
|
||||
/* Input Terminal Descriptor(4.7.2.4) */\
|
||||
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\
|
||||
/* Output Terminal Descriptor(4.7.2.5) */\
|
||||
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
|
||||
/* Feature Unit Descriptor(4.7.2.8) */\
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
|
||||
|
||||
// AUDIO simple descriptor (UAC2) for 4 microphone input
|
||||
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
|
||||
|
||||
#define TUD_AUDIO_MIC_FOUR_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CLK_SRC_LEN\
|
||||
+ TUD_AUDIO_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
|
||||
|
||||
#define TUD_AUDIO_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces
|
||||
|
||||
#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
|
||||
/* Standard Interface Association Descriptor (IAD) */\
|
||||
TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
|
||||
/* Standard AC Interface Descriptor(4.7.1) */\
|
||||
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
|
||||
TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
|
||||
/* Clock Source Descriptor(4.7.2.1) */\
|
||||
TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
|
||||
/* Input Terminal Descriptor(4.7.2.4) */\
|
||||
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\
|
||||
/* Output Terminal Descriptor(4.7.2.5) */\
|
||||
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
|
||||
/* Feature Unit Descriptor(4.7.2.8) */\
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
|
||||
|
||||
// AUDIO simple descriptor (UAC2) for mono speaker
|
||||
// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source
|
||||
|
||||
#define TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AC_LEN\
|
||||
+ TUD_AUDIO_DESC_CLK_SRC_LEN\
|
||||
+ TUD_AUDIO_DESC_INPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
|
||||
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_INT_LEN\
|
||||
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
|
||||
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN)
|
||||
|
||||
#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \
|
||||
/* Standard Interface Association Descriptor (IAD) */\
|
||||
TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
|
||||
/* Standard AC Interface Descriptor(4.7.1) */\
|
||||
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
|
||||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
|
||||
TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
|
||||
/* Clock Source Descriptor(4.7.2.1) */\
|
||||
TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
|
||||
/* Input Terminal Descriptor(4.7.2.4) */\
|
||||
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\
|
||||
/* Output Terminal Descriptor(4.7.2.5) */\
|
||||
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
|
||||
/* Feature Unit Descriptor(4.7.2.8) */\
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
|
||||
/* Standard AS Interface Descriptor(4.9.1) */\
|
||||
/* Interface 1, Alternate 1 - alternate interface for data streaming */\
|
||||
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00),\
|
||||
/* Class-Specific AS Interface Descriptor(4.9.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
|
||||
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ 1)
|
||||
|
||||
// Calculate wMaxPacketSize of Endpoints
|
||||
#define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \
|
||||
((((_maxFrequency + (TUD_OPT_HIGH_SPEED ? 7999 : 999)) / (TUD_OPT_HIGH_SPEED ? 8000 : 1000)) + 1) * _nBytesPerSample * _nChannels)
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBTMC/USB488 Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
|
||||
#define TUD_USBTMC_APP_SUBCLASS 0x03u
|
||||
|
||||
#define TUD_USBTMC_PROTOCOL_STD 0x00u
|
||||
#define TUD_USBTMC_PROTOCOL_USB488 0x01u
|
||||
|
||||
// Interface number, number of endpoints, EP string index, USB_TMC_PROTOCOL*, bulk-out endpoint ID,
|
||||
// bulk-in endpoint ID
|
||||
#define TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, _stridx, _itfProtocol) \
|
||||
/* Interface */ \
|
||||
0x09, TUSB_DESC_INTERFACE, _itfnum, 0x00, _bNumEndpoints, TUD_USBTMC_APP_CLASS, TUD_USBTMC_APP_SUBCLASS, _itfProtocol, _stridx
|
||||
|
||||
#define TUD_USBTMC_IF_DESCRIPTOR_LEN 9u
|
||||
|
||||
#define TUD_USBTMC_BULK_DESCRIPTORS(_epout, _epin, _bulk_epsize) \
|
||||
/* Endpoint Out */ \
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u, \
|
||||
/* Endpoint In */ \
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_bulk_epsize), 0u
|
||||
|
||||
#define TUD_USBTMC_BULK_DESCRIPTORS_LEN (7u+7u)
|
||||
|
||||
/* optional interrupt endpoint */ \
|
||||
// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number?
|
||||
#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), _int_pollingInterval
|
||||
|
||||
#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Vendor Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_VENDOR_DESC_LEN (9+7+7)
|
||||
|
||||
// Interface number, string index, EP Out & IN address, EP size
|
||||
#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DFU Runtime Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
|
||||
#define TUD_DFU_APP_SUBCLASS (APP_SUBCLASS_DFU_RUNTIME)
|
||||
|
||||
// Length of template descriptr: 18 bytes
|
||||
#define TUD_DFU_RT_DESC_LEN (9 + 9)
|
||||
|
||||
// DFU runtime descriptor
|
||||
// Interface number, string index, attributes, detach timeout, transfer size
|
||||
#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \
|
||||
/* Interface */ \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \
|
||||
/* Function */ \
|
||||
9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DFU Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 9 bytes + number of alternatives * 9
|
||||
#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9)
|
||||
|
||||
// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size
|
||||
// Note: Alternate count must be numeric or macro, string index is increased by one for each Alt interface
|
||||
#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \
|
||||
TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \
|
||||
/* Function */ \
|
||||
9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101)
|
||||
|
||||
#define _TUD_DFU_ALT(_itfnum, _alt, _stridx) \
|
||||
/* Interface */ \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, _alt, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx
|
||||
|
||||
#define _TUD_DFU_ALT_1(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx)
|
||||
|
||||
#define _TUD_DFU_ALT_2(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_1(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_3(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_2(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_4(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_3(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_5(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_4(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_6(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_5(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_7(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_6(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
#define _TUD_DFU_ALT_8(_itfnum, _alt_count, _stridx) \
|
||||
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
|
||||
_TUD_DFU_ALT_7(_itfnum, _alt_count+1, _stridx+1)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC-ECM Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor: 71 bytes
|
||||
#define TUD_CDC_ECM_DESC_LEN (8+9+5+5+13+7+9+9+7+7)
|
||||
|
||||
// CDC-ECM Descriptor Template
|
||||
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
|
||||
#define TUD_CDC_ECM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \
|
||||
/* Interface Association */\
|
||||
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, 0,\
|
||||
/* CDC Control Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL, 0, _desc_stridx,\
|
||||
/* CDC-ECM Header */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
|
||||
/* CDC-ECM Union */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
|
||||
/* CDC-ECM Functional Descriptor */\
|
||||
13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0,\
|
||||
/* Endpoint Notification */\
|
||||
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\
|
||||
/* CDC Data Interface (default inactive) */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
|
||||
/* CDC Data Interface (alternative active) */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// RNDIS Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if 0
|
||||
/* Windows XP */
|
||||
#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC
|
||||
#define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
|
||||
#define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */
|
||||
#else
|
||||
/* Windows 7+ */
|
||||
#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER
|
||||
#define TUD_RNDIS_ITF_SUBCLASS 0x01
|
||||
#define TUD_RNDIS_ITF_PROTOCOL 0x03
|
||||
#endif
|
||||
|
||||
// Length of template descriptor: 66 bytes
|
||||
#define TUD_RNDIS_DESC_LEN (8+9+5+5+4+5+7+9+7+7)
|
||||
|
||||
// RNDIS Descriptor Template
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
#define TUD_RNDIS_DESCRIPTOR(_itfnum, _stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize) \
|
||||
/* Interface Association */\
|
||||
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, 0,\
|
||||
/* CDC Control Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUD_RNDIS_ITF_CLASS, TUD_RNDIS_ITF_SUBCLASS, TUD_RNDIS_ITF_PROTOCOL, _stridx,\
|
||||
/* CDC-ACM Header */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\
|
||||
/* CDC Call Management */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
|
||||
/* ACM */\
|
||||
4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 0,\
|
||||
/* CDC Union */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
|
||||
/* Endpoint Notification */\
|
||||
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 1,\
|
||||
/* CDC Data Interface */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 2, TUSB_CLASS_CDC_DATA, 0, 0, 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Bluetooth Radio Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_BT_APP_CLASS (TUSB_CLASS_WIRELESS_CONTROLLER)
|
||||
#define TUD_BT_APP_SUBCLASS 0x01
|
||||
#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01
|
||||
#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02
|
||||
|
||||
// Length of template descriptor: 38 bytes + number of ISO alternatives * 23
|
||||
#define TUD_BTH_DESC_LEN (8 + 9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7))
|
||||
|
||||
/* Primary Interface */
|
||||
#define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, _stridx, \
|
||||
/* Endpoint In for events */ \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \
|
||||
/* Endpoint In for ACL data */ \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \
|
||||
/* Endpoint Out for ACL data */ \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1
|
||||
|
||||
#define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\
|
||||
/* Interface with 2 endpoints */ \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \
|
||||
/* Isochronous endpoints */ \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \
|
||||
7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1
|
||||
|
||||
#define _FIRST(a, ...) a
|
||||
#define _REST(a, ...) __VA_ARGS__
|
||||
|
||||
#define TUD_BTH_ISO_ITF_0(_itfnum, ...)
|
||||
#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__))
|
||||
#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
|
||||
TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
|
||||
#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
|
||||
TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
|
||||
#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
|
||||
TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
|
||||
#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
|
||||
TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
|
||||
#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
|
||||
TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
|
||||
|
||||
#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \
|
||||
TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__)
|
||||
|
||||
// BT Primary controller descriptor
|
||||
// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes
|
||||
// TODO BTH should also use IAD like CDC for composite device
|
||||
#define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \
|
||||
/* Interface Associate */\
|
||||
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0,\
|
||||
TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \
|
||||
TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC-NCM Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Length of template descriptor
|
||||
#define TUD_CDC_NCM_DESC_LEN (8+9+5+5+13+6+7+9+9+7+7)
|
||||
|
||||
// CDC-ECM Descriptor Template
|
||||
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
|
||||
#define TUD_CDC_NCM_DESCRIPTOR(_itfnum, _desc_stridx, _mac_stridx, _ep_notif, _ep_notif_size, _epout, _epin, _epsize, _maxsegmentsize) \
|
||||
/* Interface Association */\
|
||||
8, TUSB_DESC_INTERFACE_ASSOCIATION, _itfnum, 2, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, 0,\
|
||||
/* CDC Control Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_CDC, CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL, 0, _desc_stridx,\
|
||||
/* CDC-NCM Header */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0110),\
|
||||
/* CDC-NCM Union */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
|
||||
/* CDC-NCM Functional Descriptor */\
|
||||
13, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ETHERNET_NETWORKING, _mac_stridx, 0, 0, 0, 0, U16_TO_U8S_LE(_maxsegmentsize), U16_TO_U8S_LE(0), 0, \
|
||||
/* CDC-NCM Functional Descriptor */\
|
||||
6, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_NCM, U16_TO_U8S_LE(0x0100), 0, \
|
||||
/* Endpoint Notification */\
|
||||
7, TUSB_DESC_ENDPOINT, _ep_notif, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_notif_size), 50,\
|
||||
/* CDC Data Interface (default inactive) */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 0, 0, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\
|
||||
/* CDC Data Interface (alternative active) */\
|
||||
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum)+1), 1, 2, TUSB_CLASS_CDC_DATA, 0, NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK, 0,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_USBD_H_ */
|
||||
|
||||
/** @} */
|
||||
211
managed_components/espressif__tinyusb/src/device/usbd_control.c
Normal file
211
managed_components/espressif__tinyusb/src/device/usbd_control.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED
|
||||
|
||||
#include "dcd.h"
|
||||
#include "tusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Callback weak stubs (called if application does not provide)
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) {
|
||||
(void) rhport;
|
||||
(void) request;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
EDPT_CTRL_OUT = 0x00,
|
||||
EDPT_CTRL_IN = 0x80
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
tusb_control_request_t request;
|
||||
uint8_t* buffer;
|
||||
uint16_t data_len;
|
||||
uint16_t total_xferred;
|
||||
usbd_control_xfer_cb_t complete_cb;
|
||||
} usbd_control_xfer_t;
|
||||
|
||||
static usbd_control_xfer_t _ctrl_xfer;
|
||||
|
||||
CFG_TUD_MEM_SECTION static struct {
|
||||
TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_SIZE);
|
||||
} _ctrl_epbuf;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Queue ZLP status transaction
|
||||
static inline bool status_stage_xact(uint8_t rhport, const tusb_control_request_t* request) {
|
||||
// Opposite to endpoint in Data Phase
|
||||
const uint8_t ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
|
||||
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
|
||||
}
|
||||
|
||||
// Status phase
|
||||
bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) {
|
||||
_ctrl_xfer.request = (*request);
|
||||
_ctrl_xfer.buffer = NULL;
|
||||
_ctrl_xfer.total_xferred = 0;
|
||||
_ctrl_xfer.data_len = 0;
|
||||
|
||||
return status_stage_xact(rhport, request);
|
||||
}
|
||||
|
||||
// Queue a transaction in Data Stage
|
||||
// Each transaction has up to Endpoint0's max packet size.
|
||||
// This function can also transfer an zero-length packet
|
||||
static bool data_stage_xact(uint8_t rhport) {
|
||||
const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
|
||||
uint8_t ep_addr = EDPT_CTRL_OUT;
|
||||
|
||||
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
|
||||
ep_addr = EDPT_CTRL_IN;
|
||||
if (xact_len) {
|
||||
TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
|
||||
}
|
||||
}
|
||||
|
||||
return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len);
|
||||
}
|
||||
|
||||
// Transmit data to/from the control endpoint.
|
||||
// If the request's wLength is zero, a status packet is sent instead.
|
||||
bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) {
|
||||
_ctrl_xfer.request = (*request);
|
||||
_ctrl_xfer.buffer = (uint8_t*) buffer;
|
||||
_ctrl_xfer.total_xferred = 0U;
|
||||
_ctrl_xfer.data_len = tu_min16(len, request->wLength);
|
||||
|
||||
if (request->wLength > 0U) {
|
||||
if (_ctrl_xfer.data_len > 0U) {
|
||||
TU_ASSERT(buffer);
|
||||
}
|
||||
TU_ASSERT(data_stage_xact(rhport));
|
||||
} else {
|
||||
TU_ASSERT(status_stage_xact(rhport, request));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD API
|
||||
//--------------------------------------------------------------------+
|
||||
void usbd_control_reset(void);
|
||||
void usbd_control_set_request(const tusb_control_request_t* request);
|
||||
void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp);
|
||||
bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
void usbd_control_reset(void) {
|
||||
tu_varclr(&_ctrl_xfer);
|
||||
}
|
||||
|
||||
// Set complete callback
|
||||
void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
|
||||
_ctrl_xfer.complete_cb = fp;
|
||||
}
|
||||
|
||||
// for dcd_set_address where DCD is responsible for status response
|
||||
void usbd_control_set_request(const tusb_control_request_t* request) {
|
||||
_ctrl_xfer.request = (*request);
|
||||
_ctrl_xfer.buffer = NULL;
|
||||
_ctrl_xfer.total_xferred = 0;
|
||||
_ctrl_xfer.data_len = 0;
|
||||
}
|
||||
|
||||
// callback when a transaction complete on
|
||||
// - DATA stage of control endpoint or
|
||||
// - Status stage
|
||||
bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
// Endpoint Address is opposite to direction bit, this is Status Stage complete event
|
||||
if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) {
|
||||
TU_ASSERT(0 == xferred_bytes);
|
||||
|
||||
// invoke optional dcd hook if available
|
||||
dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
|
||||
|
||||
if (_ctrl_xfer.complete_cb) {
|
||||
// TODO refactor with usbd_driver_print_control_complete_name
|
||||
_ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
|
||||
TU_VERIFY(_ctrl_xfer.buffer);
|
||||
memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes);
|
||||
TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2);
|
||||
}
|
||||
|
||||
_ctrl_xfer.total_xferred += (uint16_t) xferred_bytes;
|
||||
_ctrl_xfer.buffer += xferred_bytes;
|
||||
|
||||
// Data Stage is complete when all request's length are transferred or
|
||||
// a short packet is sent including zero-length packet.
|
||||
if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
|
||||
(xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
|
||||
// DATA stage is complete
|
||||
bool is_ok = true;
|
||||
|
||||
// invoke complete callback if set
|
||||
// callback can still stall control in status phase e.g out data does not make sense
|
||||
if (_ctrl_xfer.complete_cb) {
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
|
||||
#endif
|
||||
|
||||
is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
|
||||
}
|
||||
|
||||
if (is_ok) {
|
||||
TU_ASSERT(status_stage_xact(rhport, &_ctrl_xfer.request));
|
||||
} else {
|
||||
// Stall both IN and OUT control endpoint
|
||||
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
|
||||
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
|
||||
}
|
||||
} else {
|
||||
// More data to transfer
|
||||
TU_ASSERT(data_stage_xact(rhport));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
142
managed_components/espressif__tinyusb/src/device/usbd_pvt.h
Normal file
142
managed_components/espressif__tinyusb/src/device/usbd_pvt.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
#ifndef TUSB_USBD_PVT_H_
|
||||
#define TUSB_USBD_PVT_H_
|
||||
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
#include "common/tusb_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef enum {
|
||||
SOF_CONSUMER_USER = 0,
|
||||
SOF_CONSUMER_AUDIO,
|
||||
} sof_consumer_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
char const* name;
|
||||
void (* init ) (void);
|
||||
bool (* deinit ) (void);
|
||||
void (* reset ) (uint8_t rhport);
|
||||
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
|
||||
bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
bool (* xfer_isr ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); // optional, return false to defer to xfer_cb()
|
||||
void (* sof ) (uint8_t rhport, uint32_t frame_count); // optional
|
||||
} usbd_class_driver_t;
|
||||
|
||||
// Invoked when initializing device stack to get additional class drivers.
|
||||
// Can be implemented by application to extend/overwrite class driver support.
|
||||
// Note: The drivers array must be accessible at all time when stack is active
|
||||
usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count);
|
||||
|
||||
typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
void usbd_int_set(bool enabled);
|
||||
void usbd_spin_lock(bool in_isr);
|
||||
void usbd_spin_unlock(bool in_isr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Endpoint API
|
||||
// Note: rhport should be 0 since device stack only support 1 rhport for now
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Open an endpoint
|
||||
bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
// Close an endpoint
|
||||
void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Submit a usb transfer
|
||||
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted
|
||||
bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
|
||||
|
||||
// Claim an endpoint before submitting a transfer.
|
||||
// If caller does not make any transfer, it must release endpoint for others.
|
||||
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Release claimed endpoint without submitting a transfer
|
||||
bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Check if endpoint is busy transferring
|
||||
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Stall endpoint
|
||||
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Clear stalled endpoint
|
||||
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Check if endpoint is stalled
|
||||
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// Allocate packet buffer used by ISO endpoints
|
||||
bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
|
||||
|
||||
// Configure and enable an ISO endpoint according to descriptor
|
||||
bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
|
||||
|
||||
// Check if endpoint is ready (not busy and not stalled)
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) {
|
||||
return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr);
|
||||
}
|
||||
|
||||
// Enable SOF interrupt
|
||||
void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Helper
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
|
||||
void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr);
|
||||
|
||||
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
228
managed_components/espressif__tinyusb/src/host/hcd.h
Normal file
228
managed_components/espressif__tinyusb/src/host/hcd.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_HCD_H_
|
||||
#define _TUSB_HCD_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Max number of endpoints pair per device
|
||||
// TODO optimize memory usage
|
||||
#ifndef CFG_TUH_ENDPOINT_MAX
|
||||
#define CFG_TUH_ENDPOINT_MAX 16
|
||||
// #ifdef TUP_HCD_ENDPOINT_MAX
|
||||
// #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX
|
||||
// #else
|
||||
// #define
|
||||
// #endif
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef enum {
|
||||
HCD_EVENT_DEVICE_ATTACH,
|
||||
HCD_EVENT_DEVICE_REMOVE,
|
||||
HCD_EVENT_XFER_COMPLETE,
|
||||
|
||||
USBH_EVENT_FUNC_CALL, // Not an HCD event
|
||||
HCD_EVENT_COUNT
|
||||
} hcd_eventid_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t rhport;
|
||||
uint8_t event_id;
|
||||
uint8_t dev_addr;
|
||||
|
||||
union {
|
||||
// Attach, Remove
|
||||
struct {
|
||||
uint8_t hub_addr;
|
||||
uint8_t hub_port;
|
||||
uint8_t speed;
|
||||
} connection;
|
||||
|
||||
// XFER_COMPLETE
|
||||
struct {
|
||||
uint8_t ep_addr;
|
||||
uint8_t result;
|
||||
uint32_t len;
|
||||
} xfer_complete;
|
||||
|
||||
// FUNC_CALL
|
||||
struct {
|
||||
void (*func) (void*);
|
||||
void* param;
|
||||
}func_call;
|
||||
};
|
||||
} hcd_event_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Memory API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// clean/flush data cache: write cache -> memory.
|
||||
// Required before an DMA TX transfer to make sure data is in memory
|
||||
bool hcd_dcache_clean(void const* addr, uint32_t data_size);
|
||||
|
||||
// invalidate data cache: mark cache as invalid, next read will read from memory
|
||||
// Required BOTH before and after an DMA RX transfer
|
||||
bool hcd_dcache_invalidate(void const* addr, uint32_t data_size);
|
||||
|
||||
// clean and invalidate data cache
|
||||
// Required before an DMA transfer where memory is both read/write by DMA
|
||||
bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// optional hcd configuration, called by tuh_configure()
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
|
||||
|
||||
// Initialize controller to host mode
|
||||
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
|
||||
|
||||
// De-initialize controller
|
||||
bool hcd_deinit(uint8_t rhport);
|
||||
|
||||
// Interrupt Handler
|
||||
void hcd_int_handler(uint8_t rhport, bool in_isr);
|
||||
|
||||
// Enable USB interrupt
|
||||
void hcd_int_enable (uint8_t rhport);
|
||||
|
||||
// Disable USB interrupt
|
||||
void hcd_int_disable(uint8_t rhport);
|
||||
|
||||
// Get frame number (1ms)
|
||||
uint32_t hcd_frame_number(uint8_t rhport);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Port API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the current connect status of roothub port
|
||||
bool hcd_port_connect_status(uint8_t rhport);
|
||||
|
||||
// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
|
||||
// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
|
||||
void hcd_port_reset(uint8_t rhport);
|
||||
|
||||
// Complete bus reset sequence, may be required by some controllers
|
||||
void hcd_port_reset_end(uint8_t rhport);
|
||||
|
||||
// Get port link speed
|
||||
tusb_speed_t hcd_port_speed_get(uint8_t rhport);
|
||||
|
||||
// HCD closes all opened endpoints belong to this device
|
||||
void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoints API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Open an endpoint
|
||||
// return true if successfully opened or endpoint is currently opened
|
||||
bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc);
|
||||
|
||||
// Close an endpoint
|
||||
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr);
|
||||
|
||||
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
|
||||
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
|
||||
|
||||
// Abort a queued transfer. Note: it can only abort transfer that has not been started
|
||||
// Return true if a queued transfer is aborted, false if there is no transfer to abort
|
||||
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
|
||||
bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]);
|
||||
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH implemented API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Called by HCD to notify stack
|
||||
extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
|
||||
|
||||
// Helper to send device attach event
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void hcd_event_device_attach(uint8_t rhport, bool in_isr) {
|
||||
hcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = HCD_EVENT_DEVICE_ATTACH;
|
||||
event.connection.hub_addr = 0;
|
||||
event.connection.hub_port = 0;
|
||||
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// Helper to send device removal event
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void hcd_event_device_remove(uint8_t rhport, bool in_isr) {
|
||||
hcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = HCD_EVENT_DEVICE_REMOVE;
|
||||
event.connection.hub_addr = 0;
|
||||
event.connection.hub_port = 0;
|
||||
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// Helper to send USB transfer event
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr) {
|
||||
hcd_event_t event = {
|
||||
.rhport = 0, // TODO correct rhport
|
||||
.event_id = HCD_EVENT_XFER_COMPLETE,
|
||||
.dev_addr = dev_addr,
|
||||
};
|
||||
event.xfer_complete.ep_addr = ep_addr;
|
||||
event.xfer_complete.result = result;
|
||||
event.xfer_complete.len = xferred_bytes;
|
||||
|
||||
hcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
478
managed_components/espressif__tinyusb/src/host/hub.c
Normal file
478
managed_components/espressif__tinyusb/src/host/hub.c
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_HUB)
|
||||
|
||||
#include "hcd.h"
|
||||
#include "usbh.h"
|
||||
#include "usbh_pvt.h"
|
||||
#include "hub.h"
|
||||
|
||||
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||
#define HUB_DEBUG 2
|
||||
#define TU_LOG_DRV(...) TU_LOG(HUB_DEBUG, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
|
||||
// from hub descriptor
|
||||
uint8_t bNbrPorts;
|
||||
uint8_t bPwrOn2PwrGood_2ms; // port power on to good, in 2ms unit
|
||||
// uint16_t wHubCharacteristics;
|
||||
|
||||
hub_port_status_response_t port_status;
|
||||
} hub_interface_t;
|
||||
|
||||
typedef struct {
|
||||
TUH_EPBUF_DEF(status_change, 4); // interrupt endpoint
|
||||
TUH_EPBUF_DEF(ctrl_buf, CFG_TUH_HUB_BUFSIZE);
|
||||
} hub_epbuf_t;
|
||||
|
||||
static tuh_xfer_cb_t user_complete_cb = NULL;
|
||||
static hub_interface_t hub_itfs[CFG_TUH_HUB];
|
||||
CFG_TUH_MEM_SECTION static hub_epbuf_t hub_epbufs[CFG_TUH_HUB];
|
||||
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline hub_interface_t* get_hub_itf(uint8_t daddr) {
|
||||
return &hub_itfs[daddr-1-CFG_TUH_DEVICE_MAX];
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline hub_epbuf_t* get_hub_epbuf(uint8_t daddr) {
|
||||
return &hub_epbufs[daddr-1-CFG_TUH_DEVICE_MAX];
|
||||
}
|
||||
|
||||
#if CFG_TUSB_DEBUG >= HUB_DEBUG
|
||||
static char const* const _hub_feature_str[] = {
|
||||
[HUB_FEATURE_PORT_CONNECTION ] = "PORT_CONNECTION",
|
||||
[HUB_FEATURE_PORT_ENABLE ] = "PORT_ENABLE",
|
||||
[HUB_FEATURE_PORT_SUSPEND ] = "PORT_SUSPEND",
|
||||
[HUB_FEATURE_PORT_OVER_CURRENT ] = "PORT_OVER_CURRENT",
|
||||
[HUB_FEATURE_PORT_RESET ] = "PORT_RESET",
|
||||
[HUB_FEATURE_PORT_POWER ] = "PORT_POWER",
|
||||
[HUB_FEATURE_PORT_LOW_SPEED ] = "PORT_LOW_SPEED",
|
||||
[HUB_FEATURE_PORT_CONNECTION_CHANGE ] = "PORT_CONNECTION_CHANGE",
|
||||
[HUB_FEATURE_PORT_ENABLE_CHANGE ] = "PORT_ENABLE_CHANGE",
|
||||
[HUB_FEATURE_PORT_SUSPEND_CHANGE ] = "PORT_SUSPEND_CHANGE",
|
||||
[HUB_FEATURE_PORT_OVER_CURRENT_CHANGE ] = "PORT_OVER_CURRENT_CHANGE",
|
||||
[HUB_FEATURE_PORT_RESET_CHANGE ] = "PORT_RESET_CHANGE",
|
||||
[HUB_FEATURE_PORT_TEST ] = "PORT_TEST",
|
||||
[HUB_FEATURE_PORT_INDICATOR ] = "PORT_INDICATOR",
|
||||
};
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HUB
|
||||
//--------------------------------------------------------------------+
|
||||
bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_CLEAR_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_LOG_DRV("HUB Clear Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_SET_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_LOG_DRV("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void port_get_status_complete (tuh_xfer_t* xfer) {
|
||||
if (xfer->result == XFER_RESULT_SUCCESS) {
|
||||
hub_interface_t* p_hub = get_hub_itf(xfer->daddr);
|
||||
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
}
|
||||
|
||||
xfer->complete_cb = user_complete_cb;
|
||||
user_complete_cb = NULL;
|
||||
if (xfer->complete_cb) {
|
||||
xfer->complete_cb(xfer);
|
||||
}
|
||||
}
|
||||
|
||||
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = (hub_port == 0) ? TUSB_REQ_RCPT_DEVICE : TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HUB_REQUEST_GET_STATUS,
|
||||
.wValue = 0,
|
||||
.wIndex = hub_port,
|
||||
.wLength = tu_htole16(4)
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = resp,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
if (hub_port != 0) {
|
||||
// intercept complete callback to save port status, ignore resp
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(hub_addr);
|
||||
xfer.complete_cb = port_get_status_complete;
|
||||
xfer.buffer = p_epbuf->ctrl_buf;
|
||||
user_complete_cb = complete_cb;
|
||||
} else {
|
||||
user_complete_cb = NULL;
|
||||
}
|
||||
|
||||
TU_LOG_DRV("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
|
||||
TU_VERIFY(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
|
||||
(void) hub_port;
|
||||
hub_interface_t* p_hub = get_hub_itf(hub_addr);
|
||||
*resp = p_hub->port_status;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CLASS-USBH API (don't require to verify parameters)
|
||||
//--------------------------------------------------------------------+
|
||||
bool hub_init(void) {
|
||||
tu_memclr(hub_itfs, sizeof(hub_itfs));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass &&
|
||||
0 == itf_desc->bInterfaceSubClass);
|
||||
TU_VERIFY(itf_desc->bInterfaceProtocol <= 1); // not support multiple TT yet
|
||||
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t);
|
||||
TU_ASSERT(drv_len <= max_len);
|
||||
|
||||
// Interrupt Status endpoint
|
||||
tusb_desc_endpoint_t const *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(tuh_edpt_open(dev_addr, desc_ep));
|
||||
|
||||
hub_interface_t* p_hub = get_hub_itf(dev_addr);
|
||||
p_hub->itf_num = itf_desc->bInterfaceNumber;
|
||||
p_hub->ep_in = desc_ep->bEndpointAddress;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hub_close(uint8_t dev_addr) {
|
||||
TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, );
|
||||
hub_interface_t* p_hub = get_hub_itf(dev_addr);
|
||||
|
||||
if (p_hub->ep_in) {
|
||||
TU_LOG_DRV(" HUB close addr = %d\r\n", dev_addr);
|
||||
tu_memclr(p_hub, sizeof( hub_interface_t));
|
||||
}
|
||||
}
|
||||
|
||||
bool hub_edpt_status_xfer(uint8_t daddr) {
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
|
||||
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hub->ep_in));
|
||||
if (!usbh_edpt_xfer(daddr, p_hub->ep_in, p_epbuf->status_change, 1)) {
|
||||
usbh_edpt_release(daddr, p_hub->ep_in);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Set Configure
|
||||
//--------------------------------------------------------------------+
|
||||
static void config_set_port_power (tuh_xfer_t* xfer);
|
||||
static void config_port_power_complete (tuh_xfer_t* xfer);
|
||||
|
||||
bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
TU_ASSERT(itf_num == p_hub->itf_num);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
|
||||
|
||||
// Get Hub Descriptor
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_DEVICE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HUB_REQUEST_GET_DESCRIPTOR,
|
||||
.wValue = 0,
|
||||
.wIndex = 0,
|
||||
.wLength = sizeof(hub_desc_cs_t)
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = p_epbuf->ctrl_buf,
|
||||
.complete_cb = config_set_port_power,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_set_port_power (tuh_xfer_t* xfer) {
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
|
||||
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
|
||||
|
||||
// only use number of ports in hub descriptor
|
||||
hub_desc_cs_t const* desc_hub = (hub_desc_cs_t const*) p_epbuf->ctrl_buf;
|
||||
p_hub->bNbrPorts = desc_hub->bNbrPorts;
|
||||
p_hub->bPwrOn2PwrGood_2ms = desc_hub->bPwrOn2PwrGood;
|
||||
|
||||
// May need to GET_STATUS
|
||||
|
||||
// Set Port Power to be able to detect connection, starting with port 1
|
||||
uint8_t const hub_port = 1;
|
||||
hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
}
|
||||
|
||||
static void config_port_power_complete (tuh_xfer_t* xfer) {
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
|
||||
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
|
||||
if (xfer->setup->wIndex == p_hub->bNbrPorts) {
|
||||
// All ports are power -> queue notification status endpoint and
|
||||
// complete the SET CONFIGURATION
|
||||
if (!hub_edpt_status_xfer(daddr)) {
|
||||
TU_MESS_FAILED();
|
||||
TU_BREAKPOINT();
|
||||
}
|
||||
usbh_driver_set_config_complete(daddr, p_hub->itf_num);
|
||||
} else {
|
||||
// power next port
|
||||
uint8_t const hub_port = (uint8_t) (xfer->setup->wIndex + 1);
|
||||
hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Connection Changes
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
STATE_IDLE = 0,
|
||||
STATE_HUB_STATUS,
|
||||
STATE_CLEAR_CHANGE,
|
||||
STATE_CHECK_CONN,
|
||||
STATE_COMPLETE
|
||||
};
|
||||
|
||||
static void process_new_status(tuh_xfer_t* xfer);
|
||||
|
||||
// callback as response of interrupt endpoint polling
|
||||
bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) xferred_bytes;
|
||||
(void) ep_addr;
|
||||
|
||||
bool processed = false; // true if new status is processed
|
||||
|
||||
if (result == XFER_RESULT_SUCCESS) {
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
hub_epbuf_t *p_epbuf = get_hub_epbuf(daddr);
|
||||
const uint8_t status_change = p_epbuf->status_change[0];
|
||||
TU_LOG_DRV(" Hub Status Change = 0x%02X\r\n", status_change);
|
||||
|
||||
if (status_change == 0) {
|
||||
// The status change event was neither for the hub, nor for any of its ports.
|
||||
// This shouldn't happen, but it does with some devices. Re-Initiate the interrupt poll.
|
||||
processed = false;
|
||||
} else if (tu_bit_test(status_change, 0)) {
|
||||
// Hub bit 0 is for the hub device events
|
||||
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
|
||||
} else {
|
||||
// Hub bits 1 to n are hub port events
|
||||
for (uint8_t port=1; port <= p_hub->bNbrPorts; port++) {
|
||||
if (tu_bit_test(status_change, port)) {
|
||||
processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
|
||||
break; // after completely processed one port, we will re-queue the status poll and handle next one
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If new status event is processed: next status pool is queued by usbh.c after handled this request
|
||||
// Otherwise re-queue the status poll here
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_new_status(tuh_xfer_t* xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
|
||||
if (xfer->result != XFER_RESULT_SUCCESS) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr),);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
hub_interface_t *p_hub = get_hub_itf(daddr);
|
||||
const uintptr_t state = xfer->user_data;
|
||||
bool processed = false; // true if new status is processed
|
||||
|
||||
switch (state) {
|
||||
case STATE_HUB_STATUS: {
|
||||
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
|
||||
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
|
||||
if (hub_status.change.local_power_source) {
|
||||
TU_LOG_DRV(" Local Power Change\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (hub_status.change.over_current) {
|
||||
TU_LOG_DRV(" Over Current\r\n");
|
||||
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_CLEAR_CHANGE:
|
||||
// Get port status complete --> clear change
|
||||
if (p_hub->port_status.change.connection) {
|
||||
// Connection change
|
||||
// Port is powered and enabled
|
||||
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
|
||||
|
||||
// Acknowledge Port Connection Change
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE,
|
||||
process_new_status, STATE_CHECK_CONN);
|
||||
} else if (p_hub->port_status.change.port_enable) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.suspend) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.over_current) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
} else if (p_hub->port_status.change.reset) {
|
||||
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE,
|
||||
process_new_status, STATE_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_CHECK_CONN: {
|
||||
const hcd_event_t event = {
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = p_hub->port_status.status.connection ? HCD_EVENT_DEVICE_ATTACH : HCD_EVENT_DEVICE_REMOVE,
|
||||
.connection = {
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
hcd_event_handler(&event, false);
|
||||
// skip status for attach event, usbh will do it after handled this enumeration
|
||||
processed = (event.event_id == HCD_EVENT_DEVICE_ATTACH);
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_COMPLETE:
|
||||
default:
|
||||
processed = false; // complete this status, queue next status
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr),);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
220
managed_components/espressif__tinyusb/src/host/hub.h
Normal file
220
managed_components/espressif__tinyusb/src/host/hub.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_HUB_H_
|
||||
#define TUSB_HUB_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef CFG_TUH_HUB_BUFSIZE
|
||||
#define CFG_TUH_HUB_BUFSIZE 12
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
HUB_REQUEST_GET_STATUS = 0 ,
|
||||
HUB_REQUEST_CLEAR_FEATURE = 1 ,
|
||||
// 2 is reserved
|
||||
HUB_REQUEST_SET_FEATURE = 3 ,
|
||||
// 4-5 are reserved
|
||||
HUB_REQUEST_GET_DESCRIPTOR = 6 ,
|
||||
HUB_REQUEST_SET_DESCRIPTOR = 7 ,
|
||||
HUB_REQUEST_CLEAR_TT_BUFFER = 8 ,
|
||||
HUB_REQUEST_RESET_TT = 9 ,
|
||||
HUB_REQUEST_GET_TT_STATE = 10 ,
|
||||
HUB_REQUEST_STOP_TT = 11
|
||||
};
|
||||
|
||||
enum {
|
||||
HUB_FEATURE_HUB_LOCAL_POWER_CHANGE = 0,
|
||||
HUB_FEATURE_HUB_OVER_CURRENT_CHANGE
|
||||
};
|
||||
|
||||
enum{
|
||||
HUB_FEATURE_PORT_CONNECTION = 0,
|
||||
HUB_FEATURE_PORT_ENABLE = 1,
|
||||
HUB_FEATURE_PORT_SUSPEND = 2,
|
||||
HUB_FEATURE_PORT_OVER_CURRENT = 3,
|
||||
HUB_FEATURE_PORT_RESET = 4,
|
||||
// 5-7 are reserved
|
||||
HUB_FEATURE_PORT_POWER = 8,
|
||||
HUB_FEATURE_PORT_LOW_SPEED = 9,
|
||||
// 10-15 are reserved
|
||||
HUB_FEATURE_PORT_CONNECTION_CHANGE = 16,
|
||||
HUB_FEATURE_PORT_ENABLE_CHANGE = 17,
|
||||
HUB_FEATURE_PORT_SUSPEND_CHANGE = 18,
|
||||
HUB_FEATURE_PORT_OVER_CURRENT_CHANGE = 19,
|
||||
HUB_FEATURE_PORT_RESET_CHANGE = 20,
|
||||
HUB_FEATURE_PORT_TEST = 21,
|
||||
HUB_FEATURE_PORT_INDICATOR = 22
|
||||
};
|
||||
|
||||
enum {
|
||||
HUB_CHARS_POWER_GANGED_SWITCHING = 0,
|
||||
HUB_CHARS_POWER_INDIVIDUAL_SWITCHING = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
HUB_CHARS_OVER_CURRENT_GLOBAL = 0,
|
||||
HUB_CHARS_OVER_CURRENT_INDIVIDUAL = 1,
|
||||
};
|
||||
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
uint8_t bNbrPorts;
|
||||
uint16_t wHubCharacteristics;
|
||||
uint8_t bPwrOn2PwrGood;
|
||||
uint8_t bHubContrCurrent;
|
||||
uint8_t DeviceRemovable; // bitmap each bit for a port (from bit1)
|
||||
uint8_t PortPwrCtrlMask; // just for compatibility, should be 0xff
|
||||
} hub_desc_cs_t;
|
||||
TU_VERIFY_STATIC(sizeof(hub_desc_cs_t) == 9, "size is not correct");
|
||||
TU_VERIFY_STATIC(CFG_TUH_HUB_BUFSIZE >= sizeof(hub_desc_cs_t), "buffer is not big enough");
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t logical_power_switching_mode : 2; // [0..1] gannged or individual power switching
|
||||
uint8_t compound_device : 1; // [2] hub is part of compound device
|
||||
uint8_t over_current_protect_mode : 2; // [3..4] global or individual port over-current protection
|
||||
uint8_t tt_think_time : 2; // [5..6] TT think time
|
||||
uint8_t port_indicator_supported : 1; // [7] port indicator supported
|
||||
};
|
||||
uint8_t rsv1;
|
||||
} hub_characteristics_t;
|
||||
TU_VERIFY_STATIC(sizeof(hub_characteristics_t) == 2, "size is not correct");
|
||||
|
||||
// data in response of HUB_REQUEST_GET_STATUS, wIndex = 0 (hub)
|
||||
typedef struct {
|
||||
union{
|
||||
struct TU_ATTR_PACKED {
|
||||
uint16_t local_power_source : 1;
|
||||
uint16_t over_current : 1;
|
||||
uint16_t : 14;
|
||||
};
|
||||
|
||||
uint16_t value;
|
||||
} status, change;
|
||||
} hub_status_response_t;
|
||||
TU_VERIFY_STATIC( sizeof(hub_status_response_t) == 4, "size is not correct");
|
||||
|
||||
// data in response of HUB_REQUEST_GET_STATUS, wIndex = Port num
|
||||
typedef struct {
|
||||
union TU_ATTR_PACKED {
|
||||
struct TU_ATTR_PACKED {
|
||||
// Bit 0-4 are for change & status
|
||||
uint16_t connection : 1; // [0] 0 = no device, 1 = device connected
|
||||
uint16_t port_enable : 1; // [1] port is enabled
|
||||
uint16_t suspend : 1; // [2]
|
||||
uint16_t over_current : 1; // [3] over-current exists
|
||||
uint16_t reset : 1; // [4] 0 = no reset, 1 = resetting
|
||||
|
||||
// From Bit 5 are for status only
|
||||
uint16_t rsv5_7 : 3; // [5..7] reserved
|
||||
uint16_t port_power : 1; // [8] 0 = port is off, 1 = port is on
|
||||
uint16_t low_speed : 1; // [9] low speed device attached
|
||||
uint16_t high_speed : 1; // [10] high speed device attached
|
||||
uint16_t port_test_mode : 1; // [11] port in test mode
|
||||
uint16_t port_indicator_control : 1; // [12] 0: default color, 1: indicator is software controlled
|
||||
uint16_t TU_RESERVED : 3; // [13..15] reserved
|
||||
};
|
||||
|
||||
uint16_t value;
|
||||
} status, change;
|
||||
} hub_port_status_response_t;
|
||||
TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HUB API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Clear port feature
|
||||
bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Set port feature
|
||||
bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get port status
|
||||
// If hub_port != 0, resp is ignored. hub_port_get_status_local() can be used to retrieve the status
|
||||
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get port status from local cache. This does not send a request to the device
|
||||
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp);
|
||||
|
||||
// Get status from Interrupt endpoint
|
||||
bool hub_edpt_status_xfer(uint8_t daddr);
|
||||
|
||||
// Reset a port
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Clear Port Reset Change
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Get Hub status (port = 0)
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_get_status(uint8_t hub_addr, void* resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_get_status(hub_addr, 0, resp, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Clear Hub feature
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_clear_feature(uint8_t hub_addr, uint8_t feature, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_clear_feature(hub_addr, 0, feature, complete_cb, user_data);
|
||||
}
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
bool hub_init (void);
|
||||
bool hub_deinit (void);
|
||||
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool hub_set_config (uint8_t daddr, uint8_t itf_num);
|
||||
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void hub_close (uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1939
managed_components/espressif__tinyusb/src/host/usbh.c
Normal file
1939
managed_components/espressif__tinyusb/src/host/usbh.c
Normal file
File diff suppressed because it is too large
Load Diff
393
managed_components/espressif__tinyusb/src/host/usbh.h
Normal file
393
managed_components/espressif__tinyusb/src/host/usbh.h
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_USBH_H_
|
||||
#define _TUSB_USBH_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#if CFG_TUH_MAX3421
|
||||
#include "portable/analog/max3421/hcd_max3421.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Endpoint Bulk size depending on host mx speed
|
||||
#define TUH_EPSIZE_BULK_MPS (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
|
||||
|
||||
// forward declaration
|
||||
struct tuh_xfer_s;
|
||||
typedef struct tuh_xfer_s tuh_xfer_t;
|
||||
typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
|
||||
|
||||
// Note1: layout and order of this will be changed in near future
|
||||
// it is advised to initialize it using member name
|
||||
// Note2: not all field is available/meaningful in callback,
|
||||
// some info is not saved by usbh to save SRAM
|
||||
struct tuh_xfer_s {
|
||||
uint8_t daddr;
|
||||
uint8_t ep_addr;
|
||||
uint8_t TU_RESERVED; // reserved
|
||||
xfer_result_t result;
|
||||
|
||||
uint32_t actual_len; // excluding setup packet
|
||||
|
||||
union {
|
||||
tusb_control_request_t const* setup; // setup packet pointer if control transfer
|
||||
uint32_t buflen; // expected length if not control transfer (not available in callback)
|
||||
};
|
||||
|
||||
uint8_t* buffer; // not available in callback if not control transfer
|
||||
tuh_xfer_cb_t complete_cb;
|
||||
uintptr_t user_data;
|
||||
|
||||
// uint32_t timeout_ms; // place holder, not supported yet
|
||||
};
|
||||
|
||||
// Subject to change
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
tusb_desc_interface_t desc;
|
||||
} tuh_itf_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t rhport;
|
||||
uint8_t hub_addr;
|
||||
uint8_t hub_port;
|
||||
uint8_t speed;
|
||||
} tuh_bus_info_t;
|
||||
|
||||
// backward compatibility for hcd_devtree_info_t, maybe removed in the future
|
||||
#define hcd_devtree_info_t tuh_bus_info_t
|
||||
#define hcd_devtree_get_info(_daddr, _bus_info) tuh_bus_info_get(_daddr, _bus_info)
|
||||
|
||||
// ConfigID for tuh_configure()
|
||||
enum {
|
||||
TUH_CFGID_INVALID = 0,
|
||||
TUH_CFGID_RPI_PIO_USB_CONFIGURATION = 100, // cfg_param: pio_usb_configuration_t
|
||||
TUH_CFGID_MAX3421 = 200,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage
|
||||
uint8_t cpuctl; // R16: CPU Control Register
|
||||
uint8_t pinctl; // R17: Pin Control Register. FDUPSPI bit is ignored
|
||||
} tuh_configure_max3421_t;
|
||||
|
||||
typedef union {
|
||||
// For TUH_CFGID_RPI_PIO_USB_CONFIGURATION use pio_usb_configuration_t
|
||||
|
||||
tuh_configure_max3421_t max3421;
|
||||
} tuh_configure_param_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION CALLBACK
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when enumeration get device descriptor
|
||||
// Device is not ready to communicate yet, application can copy the descriptor if needed
|
||||
void tuh_enum_descriptor_device_cb(uint8_t daddr, const tusb_desc_device_t *desc_device);
|
||||
|
||||
// Invoked when enumeration get configuration descriptor
|
||||
// For multi-configuration device return false to skip, true to proceed with this configuration (may not be implemented yet)
|
||||
// Device is not ready to communicate yet, application can copy the descriptor if needed
|
||||
bool tuh_enum_descriptor_configuration_cb(uint8_t daddr, uint8_t cfg_index, const tusb_desc_configuration_t *desc_config);
|
||||
|
||||
// Invoked when a device is mounted (configured)
|
||||
void tuh_mount_cb (uint8_t daddr);
|
||||
|
||||
// Invoked when a device failed to mount during enumeration process
|
||||
// void tuh_mount_failed_cb (uint8_t daddr);
|
||||
|
||||
// Invoked when a device is unmounted (detached)
|
||||
void tuh_umount_cb(uint8_t daddr);
|
||||
|
||||
// Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext()
|
||||
void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Configure host stack behavior with dynamic or port-specific parameters.
|
||||
// Should be called before tuh_init()
|
||||
// - cfg_id : configure ID (TBD)
|
||||
// - cfg_param: configure data, structure depends on the ID
|
||||
bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
|
||||
|
||||
// New API to replace tuh_init() to init host stack on specific roothub port
|
||||
bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
|
||||
|
||||
// Init host stack
|
||||
#if TUSB_VERSION_NUMBER > 2000 // 0.20.0
|
||||
TU_ATTR_DEPRECATED("Please use tusb_init(rhport, rh_init) instead")
|
||||
#endif
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_init(uint8_t rhport) {
|
||||
const tusb_rhport_init_t rh_init = {
|
||||
.role = TUSB_ROLE_HOST,
|
||||
.speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL,
|
||||
};
|
||||
return tuh_rhport_init(rhport, &rh_init);
|
||||
}
|
||||
|
||||
// Deinit host stack on rhport
|
||||
bool tuh_deinit(uint8_t rhport);
|
||||
|
||||
// Check if host stack is already initialized with any roothub ports
|
||||
// To check if an rhport is initialized, use tuh_rhport_is_active()
|
||||
bool tuh_inited(void);
|
||||
|
||||
// Task function should be called in main/rtos loop, extended version of tuh_task()
|
||||
// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever
|
||||
// - in_isr: if function is called in ISR
|
||||
void tuh_task_ext(uint32_t timeout_ms, bool in_isr);
|
||||
|
||||
// Task function should be called in main/rtos loop
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tuh_task(void) {
|
||||
tuh_task_ext(UINT32_MAX, false);
|
||||
}
|
||||
|
||||
// Check if there is pending events need processing by tuh_task()
|
||||
bool tuh_task_event_ready(void);
|
||||
|
||||
#ifndef _TUSB_HCD_H_
|
||||
extern void hcd_int_handler(uint8_t rhport, bool in_isr);
|
||||
#endif
|
||||
|
||||
// Interrupt handler alias to HCD with in_isr as optional parameter
|
||||
#define _tuh_int_handler_arg0() TU_VERIFY_STATIC(false, "tuh_int_handler() must have 1 or 2 arguments")
|
||||
#define _tuh_int_handler_arg1(_rhport) hcd_int_handler(_rhport, true)
|
||||
#define _tuh_int_handler_arg2(_rhport, _in_isr) hcd_int_handler(_rhport, _in_isr)
|
||||
|
||||
// 1st argument is rhport (mandatory), 2nd argument in_isr (optional)
|
||||
#define tuh_int_handler(...) TU_FUNC_OPTIONAL_ARG(_tuh_int_handler, __VA_ARGS__)
|
||||
|
||||
// Check if roothub port is initialized and active as a host
|
||||
bool tuh_rhport_is_active(uint8_t rhport);
|
||||
|
||||
// Assert/de-assert Bus Reset signal to roothub port. USB specs: it should last 10-50ms
|
||||
bool tuh_rhport_reset_bus(uint8_t rhport, bool active);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get VID/PID of device
|
||||
bool tuh_vid_pid_get(uint8_t daddr, uint16_t* vid, uint16_t* pid);
|
||||
|
||||
// Get local (cached) device descriptor once device is enumerated
|
||||
bool tuh_descriptor_get_device_local(uint8_t daddr, tusb_desc_device_t* desc_device);
|
||||
|
||||
// Get speed of device
|
||||
tusb_speed_t tuh_speed_get(uint8_t daddr);
|
||||
|
||||
// Check if device is connected and configured
|
||||
bool tuh_mounted(uint8_t daddr);
|
||||
|
||||
// Check if device is connected which mean device has at least 1 successful transfer
|
||||
// Note: It may not be addressed/configured/mounted yet
|
||||
bool tuh_connected(uint8_t daddr);
|
||||
|
||||
// Check if device is suspended
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_suspended(uint8_t daddr) {
|
||||
// TODO implement suspend & resume on host
|
||||
(void) daddr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if device is ready to communicate with
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_ready(uint8_t daddr) {
|
||||
return tuh_mounted(daddr) && !tuh_suspended(daddr);
|
||||
}
|
||||
|
||||
// Get bus information of device
|
||||
bool tuh_bus_info_get(uint8_t daddr, tuh_bus_info_t* bus_info);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Transfer API
|
||||
// Each Function will make a USB transfer request to device. If
|
||||
// - complete_cb != NULL, the function will return immediately and invoke the callback when request is complete.
|
||||
// - complete_cb == NULL, the function will block until request is complete.
|
||||
// In this case, user_data should be tusb_xfer_result_t* to hold the transfer result.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Helper to make Sync API from async one
|
||||
#define TU_API_SYNC(_async_api, ...) \
|
||||
xfer_result_t result = XFER_RESULT_INVALID;\
|
||||
TU_VERIFY(_async_api(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
|
||||
return result
|
||||
|
||||
// Submit a control transfer
|
||||
// - async: complete callback invoked when finished.
|
||||
// - sync : blocking if complete callback is NULL.
|
||||
bool tuh_control_xfer(tuh_xfer_t* xfer);
|
||||
|
||||
// Submit a bulk/interrupt transfer
|
||||
// - async: complete callback invoked when finished.
|
||||
// - sync : blocking if complete callback is NULL.
|
||||
bool tuh_edpt_xfer(tuh_xfer_t* xfer);
|
||||
|
||||
// Open a non-control endpoint
|
||||
bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
// Close a non-control endpoint, it will abort any pending transfer
|
||||
bool tuh_edpt_close(uint8_t daddr, uint8_t ep_addr);
|
||||
|
||||
// Abort a queued transfer. Note: it can only abort transfer that has not been started
|
||||
// Return true if a queued transfer is aborted, false if there is no transfer to abort
|
||||
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
|
||||
|
||||
// Set Address (control transfer)
|
||||
bool tuh_address_set(uint8_t daddr, uint8_t new_addr,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Set Configuration (control transfer)
|
||||
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
|
||||
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Set Interface (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
|
||||
bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors Asynchronous (non-blocking)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get an descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get device descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get configuration descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get HID report descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// Blocking if complete callback is NULL, in this case 'user_data' must contain xfer_result_t variable
|
||||
bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get language id string descriptor (control transfer)
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_descriptor_get_string_langid(uint8_t daddr, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return tuh_descriptor_get_string(daddr, 0, 0, buffer, len, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Get manufacturer string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get product string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get serial string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors Synchronous (blocking)
|
||||
// Sync API which is blocking until transfer is complete.
|
||||
// return transfer result
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Sync version of tuh_descriptor_get()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get, daddr, type, index, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_device()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_device, daddr, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_configuration()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_configuration, daddr, index, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_hid_report()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_string()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_string_langid()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_string_langid_sync(uint8_t daddr, void* buffer, uint16_t len) {
|
||||
return tuh_descriptor_get_string_sync(daddr, 0, 0, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_manufacturer_string()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_product_string()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
|
||||
}
|
||||
|
||||
// Sync version of tuh_descriptor_get_serial_string()
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
|
||||
TU_API_SYNC(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
104
managed_components/espressif__tinyusb/src/host/usbh_pvt.h
Normal file
104
managed_components/espressif__tinyusb/src/host/usbh_pvt.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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_USBH_PVT_H_
|
||||
#define _TUSB_USBH_PVT_H_
|
||||
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
#include "common/tusb_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TU_LOG_USBH(...) TU_LOG(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_MEM_USBH(...) TU_LOG_MEM(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_BUF_USBH(...) TU_LOG_BUF(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_INT_USBH(...) TU_LOG_INT(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_HEX_USBH(...) TU_LOG_HEX(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
char const* name;
|
||||
bool (* const init )(void);
|
||||
bool (* const deinit )(void);
|
||||
bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
|
||||
bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void (* const close )(uint8_t dev_addr);
|
||||
} usbh_class_driver_t;
|
||||
|
||||
// Invoked when initializing host stack to get additional class drivers.
|
||||
// Can be implemented by application to extend/overwrite class driver support.
|
||||
// Note: The drivers array must be accessible at all time when stack is active
|
||||
usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count);
|
||||
|
||||
// Call by class driver to tell USBH that it has complete the enumeration
|
||||
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num);
|
||||
|
||||
uint8_t usbh_get_rhport(uint8_t daddr);
|
||||
|
||||
uint8_t* usbh_get_enum_buf(void);
|
||||
|
||||
void usbh_int_set(bool enabled);
|
||||
|
||||
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr);
|
||||
|
||||
void usbh_spin_lock(bool in_isr);
|
||||
void usbh_spin_unlock(bool in_isr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Submit a usb transfer with callback support, require CFG_TUH_API_EDPT_XFER
|
||||
bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) {
|
||||
return usbh_edpt_xfer_with_callback(dev_addr, ep_addr, buffer, total_bytes, NULL, 0);
|
||||
}
|
||||
|
||||
// Claim an endpoint before submitting a transfer.
|
||||
// If caller does not make any transfer, it must release endpoint for others.
|
||||
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
// Release claimed endpoint without submitting a transfer
|
||||
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
// Check if endpoint transferring is complete
|
||||
bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
105
managed_components/espressif__tinyusb/src/osal/osal.h
Normal file
105
managed_components/espressif__tinyusb/src/osal/osal.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OSAL_H_
|
||||
#define _TUSB_OSAL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
typedef void (*osal_task_func_t)( void * );
|
||||
|
||||
// Timeout
|
||||
#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately
|
||||
#define OSAL_TIMEOUT_NORMAL (10) // Default timeout
|
||||
#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever
|
||||
#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER
|
||||
|
||||
// Mutex is required when using a preempted RTOS or MCU has multiple cores
|
||||
#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE
|
||||
#define OSAL_MUTEX_REQUIRED 0
|
||||
#define OSAL_MUTEX_DEF(_name) uint8_t :0
|
||||
#else
|
||||
#define OSAL_MUTEX_REQUIRED 1
|
||||
#define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name
|
||||
#endif
|
||||
|
||||
// OS thin implementation
|
||||
#if CFG_TUSB_OS == OPT_OS_NONE
|
||||
#include "osal_none.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
|
||||
#include "osal_freertos.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_MYNEWT
|
||||
#include "osal_mynewt.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_PICO
|
||||
#include "osal_pico.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_RTTHREAD
|
||||
#include "osal_rtthread.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_RTX4
|
||||
#include "osal_rtx4.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_ZEPHYR
|
||||
#include "osal_zephyr.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_CUSTOM
|
||||
#include "tusb_os_custom.h" // implemented by application
|
||||
#else
|
||||
#error OS is not supported yet
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// OSAL Porting API
|
||||
// Should be implemented as static inline function in osal_port.h header
|
||||
/*
|
||||
void osal_spin_init(osal_spinlock_t *ctx);
|
||||
void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr)
|
||||
void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr);
|
||||
|
||||
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
|
||||
bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
|
||||
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
|
||||
bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
|
||||
void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
|
||||
|
||||
osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
|
||||
bool osal_mutex_delete(osal_mutex_t mutex_hdl)
|
||||
bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
|
||||
bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
|
||||
|
||||
osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
|
||||
bool osal_queue_delete(osal_queue_t qhdl);
|
||||
bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
|
||||
bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
|
||||
bool osal_queue_empty(osal_queue_t qhdl);
|
||||
*/
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_OSAL_H_ */
|
||||
272
managed_components/espressif__tinyusb/src/osal/osal_freertos.h
Normal file
272
managed_components/espressif__tinyusb/src/osal/osal_freertos.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_OSAL_FREERTOS_H_
|
||||
#define TUSB_OSAL_FREERTOS_H_
|
||||
|
||||
// FreeRTOS Headers
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,FreeRTOS.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,semphr.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,queue.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,task.h)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
typedef StaticSemaphore_t osal_semaphore_def_t;
|
||||
typedef StaticSemaphore_t osal_mutex_def_t;
|
||||
#else
|
||||
|
||||
// not used therefore defined to the smallest possible type to save space
|
||||
typedef uint8_t osal_semaphore_def_t;
|
||||
typedef uint8_t osal_mutex_def_t;
|
||||
#endif
|
||||
|
||||
typedef SemaphoreHandle_t osal_semaphore_t;
|
||||
typedef SemaphoreHandle_t osal_mutex_t;
|
||||
typedef QueueHandle_t osal_queue_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void* buf;
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
char const* name;
|
||||
#endif
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
StaticQueue_t sq;
|
||||
#endif
|
||||
} osal_queue_def_t;
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
#define _OSAL_Q_NAME(_name) .name = #_name
|
||||
#else
|
||||
#define _OSAL_Q_NAME(_name)
|
||||
#endif
|
||||
|
||||
// _int_set is not used with an RTOS
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth];\
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) }
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
|
||||
if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return portMAX_DELAY; }
|
||||
if (msec == 0) { return 0; }
|
||||
|
||||
uint32_t ticks = pdMS_TO_TICKS(msec);
|
||||
|
||||
// If configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms, we still need to delay at least 1 tick
|
||||
if (ticks == 0) { ticks = 1; }
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
vTaskDelay(pdMS_TO_TICKS(msec));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
#if TUSB_MCU_VENDOR_ESPRESSIF
|
||||
// Espressif critical take spinlock as argument and does not use in_isr
|
||||
typedef portMUX_TYPE osal_spinlock_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
spinlock_initialize(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
portENTER_CRITICAL(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
portEXIT_CRITICAL(ctx);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef UBaseType_t osal_spinlock_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
(void) ctx;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE) {
|
||||
(void) ctx;
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
*ctx = taskENTER_CRITICAL_FROM_ISR();
|
||||
} else {
|
||||
taskENTER_CRITICAL();
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE) {
|
||||
(void) ctx;
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
taskEXIT_CRITICAL_FROM_ISR(*ctx);
|
||||
} else {
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
return xSemaphoreCreateBinaryStatic(semdef);
|
||||
#else
|
||||
(void) semdef;
|
||||
return xSemaphoreCreateBinary();
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
vSemaphoreDelete(semd_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
if (!in_isr) {
|
||||
return xSemaphoreGive(sem_hdl) != 0;
|
||||
} else {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
return res != 0;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return xSemaphoreTake(sem_hdl, _osal_ms2tick(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
xQueueReset(sem_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
return xSemaphoreCreateMutexStatic(mdef);
|
||||
#else
|
||||
(void) mdef;
|
||||
return xSemaphoreCreateMutex();
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
vSemaphoreDelete(mutex_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return osal_semaphore_wait(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return xSemaphoreGive(mutex_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
osal_queue_t q;
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
q = xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
|
||||
#else
|
||||
q = xQueueCreate(qdef->depth, qdef->item_sz);
|
||||
#endif
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
vQueueAddToRegistry(q, qdef->name);
|
||||
#endif
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
vQueueDelete(qhdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
return xQueueReceive(qhdl, data, _osal_ms2tick(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
|
||||
if (!in_isr) {
|
||||
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
|
||||
} else {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
return res != 0;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return uxQueueMessagesWaiting(qhdl) == 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
203
managed_components/espressif__tinyusb/src/osal/osal_mynewt.h
Normal file
203
managed_components/espressif__tinyusb/src/osal/osal_mynewt.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef OSAL_MYNEWT_H_
|
||||
#define OSAL_MYNEWT_H_
|
||||
|
||||
#include "os/os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
os_time_delay( os_time_ms_to_ticks32(msec) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef os_sr_t osal_spinlock_t;
|
||||
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
(void) ctx;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
OS_ENTER_CRITICAL(*ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
OS_EXIT_CRITICAL(*ctx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct os_sem osal_semaphore_def_t;
|
||||
typedef struct os_sem* osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
return (os_sem_init(semdef, 0) == OS_OK) ? (osal_semaphore_t) semdef : NULL;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
return os_sem_release(sem_hdl) == OS_OK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
|
||||
return os_sem_pend(sem_hdl, ticks) == OS_OK;
|
||||
}
|
||||
|
||||
static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct os_mutex osal_mutex_def_t;
|
||||
typedef struct os_mutex* osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
return (os_mutex_init(mdef) == OS_OK) ? (osal_mutex_t) mdef : NULL;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
|
||||
return os_mutex_pend(mutex_hdl, ticks) == OS_OK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return os_mutex_release(mutex_hdl) == OS_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth];\
|
||||
static struct os_event _name##_##evbuf[_depth];\
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, .evbuf = _name##_##evbuf};\
|
||||
|
||||
typedef struct {
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void* buf;
|
||||
void* evbuf;
|
||||
|
||||
struct os_mempool mpool;
|
||||
struct os_mempool epool;
|
||||
|
||||
struct os_eventq evq;
|
||||
}osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
if ( OS_OK != os_mempool_init(&qdef->mpool, qdef->depth, qdef->item_sz, qdef->buf, "usb queue") ) return NULL;
|
||||
if ( OS_OK != os_mempool_init(&qdef->epool, qdef->depth, sizeof(struct os_event), qdef->evbuf, "usb evqueue") ) return NULL;
|
||||
|
||||
os_eventq_init(&qdef->evq);
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
(void) qhdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
(void) msec; // os_eventq_get() does not take timeout, always behave as msec = WAIT_FOREVER
|
||||
|
||||
struct os_event* ev;
|
||||
ev = os_eventq_get(&qhdl->evq);
|
||||
|
||||
memcpy(data, ev->ev_arg, qhdl->item_sz); // copy message
|
||||
os_memblock_put(&qhdl->mpool, ev->ev_arg); // put back mem block
|
||||
os_memblock_put(&qhdl->epool, ev); // put back ev block
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr) {
|
||||
(void) in_isr;
|
||||
|
||||
// get a block from mem pool for data
|
||||
void* ptr = os_memblock_get(&qhdl->mpool);
|
||||
if (!ptr) return false;
|
||||
memcpy(ptr, data, qhdl->item_sz);
|
||||
|
||||
// get a block from event pool to put into queue
|
||||
struct os_event* ev = (struct os_event*) os_memblock_get(&qhdl->epool);
|
||||
if (!ev) {
|
||||
os_memblock_put(&qhdl->mpool, ptr);
|
||||
return false;
|
||||
}
|
||||
tu_memclr(ev, sizeof(struct os_event));
|
||||
ev->ev_arg = ptr;
|
||||
|
||||
os_eventq_put(&qhdl->evq, ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return STAILQ_EMPTY(&qhdl->evq.evq_list);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OSAL_MYNEWT_H_ */
|
||||
202
managed_components/espressif__tinyusb/src/osal/osal_none.h
Normal file
202
managed_components/espressif__tinyusb/src/osal/osal_none.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_OSAL_NONE_H_
|
||||
#define TUSB_OSAL_NONE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
void (* interrupt_set)(bool);
|
||||
} osal_spinlock_t;
|
||||
|
||||
// For SMP, spinlock must be locked by hardware, cannot just use interrupt
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name = { .interrupt_set = _int_set }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
(void) ctx;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!in_isr) {
|
||||
ctx->interrupt_set(false);
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!in_isr) {
|
||||
ctx->interrupt_set(true);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
volatile uint16_t count;
|
||||
} osal_semaphore_def_t;
|
||||
|
||||
typedef osal_semaphore_def_t* osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
semdef->count = 0;
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
sem_hdl->count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO blocking for now
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
(void) msec;
|
||||
|
||||
while (sem_hdl->count == 0) {}
|
||||
sem_hdl->count--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
sem_hdl->count = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API
|
||||
// Within tinyusb, mutex is never used in ISR context
|
||||
//--------------------------------------------------------------------+
|
||||
typedef osal_semaphore_def_t osal_mutex_def_t;
|
||||
typedef osal_semaphore_t osal_mutex_t;
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
// Note: multiple cores MCUs usually do provide IPC API for mutex
|
||||
// or we can use std atomic function
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
mdef->count = 1;
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return osal_semaphore_wait(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return osal_semaphore_post(mutex_hdl, false);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define osal_mutex_create(_mdef) (NULL)
|
||||
#define osal_mutex_lock(_mutex_hdl, _ms) (true)
|
||||
#define osal_mutex_unlock(_mutex_hdl) (true)
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
typedef struct {
|
||||
void (* interrupt_set)(bool);
|
||||
tu_fifo_t ff;
|
||||
} osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
// _int_set is used as mutex in OS NONE (disable/enable USB ISR)
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
osal_queue_def_t _name = { \
|
||||
.interrupt_set = _int_set, \
|
||||
.ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
tu_fifo_clear(&qdef->ff);
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
(void) qhdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
(void) msec; // not used, always behave as msec = 0
|
||||
|
||||
qhdl->interrupt_set(false);
|
||||
const bool success = tu_fifo_read(&qhdl->ff, data);
|
||||
qhdl->interrupt_set(true);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
|
||||
if (!in_isr) {
|
||||
qhdl->interrupt_set(false);
|
||||
}
|
||||
|
||||
const bool success = tu_fifo_write(&qhdl->ff, data);
|
||||
|
||||
if (!in_isr) {
|
||||
qhdl->interrupt_set(true);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
// Skip queue lock/unlock since this function is primarily called
|
||||
// with interrupt disabled before going into low power mode
|
||||
return tu_fifo_empty(&qhdl->ff);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
185
managed_components/espressif__tinyusb/src/osal/osal_pico.h
Normal file
185
managed_components/espressif__tinyusb/src/osal/osal_pico.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* 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_OSAL_PICO_H_
|
||||
#define TUSB_OSAL_PICO_H_
|
||||
|
||||
#include "pico/time.h"
|
||||
#include "pico/sem.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/critical_section.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
sleep_ms(msec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef critical_section_t osal_spinlock_t; // pico implement critical section with spinlock
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
critical_section_init(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
(void) in_isr;
|
||||
critical_section_enter_blocking(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
(void) in_isr;
|
||||
critical_section_exit(ctx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct semaphore osal_semaphore_def_t, * osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
sem_init(semdef, 0, 255);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
sem_release(sem_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return sem_acquire_timeout_ms(sem_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
sem_reset(sem_hdl, 0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API
|
||||
// Within tinyusb, mutex is never used in ISR context
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct mutex osal_mutex_def_t, * osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
mutex_init(mdef);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return mutex_enter_timeout_ms(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
mutex_exit(mutex_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
typedef struct {
|
||||
tu_fifo_t ff;
|
||||
struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
|
||||
} osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
osal_queue_def_t _name = { \
|
||||
.ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
critical_section_init(&qdef->critsec);
|
||||
tu_fifo_clear(&qdef->ff);
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
osal_queue_def_t* qdef = (osal_queue_def_t*) qhdl;
|
||||
critical_section_deinit(&qdef->critsec);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
(void) msec; // not used, always behave as msec = 0
|
||||
|
||||
critical_section_enter_blocking(&qhdl->critsec);
|
||||
bool success = tu_fifo_read(&qhdl->ff, data);
|
||||
critical_section_exit(&qhdl->critsec);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
|
||||
(void) in_isr;
|
||||
|
||||
critical_section_enter_blocking(&qhdl->critsec);
|
||||
bool success = tu_fifo_write(&qhdl->ff, data);
|
||||
critical_section_exit(&qhdl->critsec);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
// TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
|
||||
// volatile read.
|
||||
|
||||
// Skip queue lock/unlock since this function is primarily called
|
||||
// with interrupt disabled before going into low power mode
|
||||
return tu_fifo_empty(&qhdl->ff);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
174
managed_components/espressif__tinyusb/src/osal/osal_rtthread.h
Normal file
174
managed_components/espressif__tinyusb/src/osal/osal_rtthread.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 tfx2001 (2479727366@qq.com)
|
||||
* Copyright (c) 2020 yekai (2857693944@qq.com)
|
||||
*
|
||||
* 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_OSAL_RTTHREAD_H_
|
||||
#define TUSB_OSAL_RTTHREAD_H_
|
||||
|
||||
// RT-Thread Headers
|
||||
#include "rtthread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
rt_thread_mdelay(msec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct rt_spinlock osal_spinlock_t;
|
||||
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
rt_spin_lock_init(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
rt_spin_lock(ctx);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
rt_spin_unlock(ctx);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct rt_semaphore osal_semaphore_def_t;
|
||||
typedef rt_sem_t osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
|
||||
rt_sem_init(semdef, "tusb", 0, RT_IPC_FLAG_PRIO);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
return RT_EOK == rt_sem_detach(semd_hdl);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
return rt_sem_release(sem_hdl) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return rt_sem_take(sem_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
rt_sem_control(sem_hdl, RT_IPC_CMD_RESET, 0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct rt_mutex osal_mutex_def_t;
|
||||
typedef rt_mutex_t osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
|
||||
rt_mutex_init(mdef, "tusb", RT_IPC_FLAG_PRIO);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
return RT_EOK == rt_mutex_detach(mutex_hdl);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return rt_mutex_take(mutex_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return rt_mutex_release(mutex_hdl) == RT_EOK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth]; \
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
|
||||
|
||||
typedef struct {
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void *buf;
|
||||
|
||||
struct rt_messagequeue sq;
|
||||
} osal_queue_def_t;
|
||||
|
||||
typedef rt_mq_t osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t *qdef) {
|
||||
rt_mq_init(&(qdef->sq), "tusb", qdef->buf, qdef->item_sz,
|
||||
qdef->item_sz * qdef->depth, RT_IPC_FLAG_PRIO);
|
||||
return &(qdef->sq);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
return RT_EOK == rt_mq_detach(qhdl);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec) {
|
||||
rt_tick_t tick = rt_tick_from_millisecond((rt_int32_t) msec);
|
||||
#if RT_VERSION_MAJOR >= 5
|
||||
return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) > 0;
|
||||
#else
|
||||
return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) == RT_EOK;
|
||||
#endif /* RT_VERSION_MAJOR >= 5 */
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
|
||||
(void) in_isr;
|
||||
return rt_mq_send(qhdl, (void *)data, qhdl->msg_size) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return (qhdl->entry) == 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
192
managed_components/espressif__tinyusb/src/osal/osal_rtx4.h
Normal file
192
managed_components/espressif__tinyusb/src/osal/osal_rtx4.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Tian Yunhao (t123yh)
|
||||
* 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_OSAL_RTX4_H_
|
||||
#define TUSB_OSAL_RTX4_H_
|
||||
|
||||
#include <rtl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
uint16_t hi = msec >> 16;
|
||||
uint16_t lo = msec;
|
||||
while (hi--) {
|
||||
os_dly_wait(0xFFFE);
|
||||
}
|
||||
os_dly_wait(lo);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) {
|
||||
if (msec == OSAL_TIMEOUT_WAIT_FOREVER) {
|
||||
return 0xFFFF;
|
||||
} else if (msec >= 0xFFFE) {
|
||||
return 0xFFFE;
|
||||
} else {
|
||||
return msec;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API, stub not implemented
|
||||
//--------------------------------------------------------------------+
|
||||
typedef uint8_t osal_spinlock_t;
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
(void) ctx;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
(void) ctx; (void) in_isr;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
(void) ctx; (void) in_isr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef OS_SEM osal_semaphore_def_t;
|
||||
typedef OS_ID osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline OS_ID osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
os_sem_init(semdef, 0);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
os_sem_send(sem_hdl);
|
||||
} else {
|
||||
isr_sem_send(sem_hdl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return os_sem_wait(sem_hdl, msec2wait(msec)) != OS_R_TMO;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef OS_MUT osal_mutex_def_t;
|
||||
typedef OS_ID osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
os_mut_init(mdef);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return os_mut_wait(mutex_hdl, msec2wait(msec)) != OS_R_TMO;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return os_mut_release(mutex_hdl) == OS_R_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
os_mbx_declare(_name##__mbox, _depth); \
|
||||
_declare_box(_name##__pool, sizeof(_type), _depth); \
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .pool = _name##__pool, .mbox = _name##__mbox };
|
||||
|
||||
typedef struct {
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
U32* pool;
|
||||
U32* mbox;
|
||||
}osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
os_mbx_init(qdef->mbox, (qdef->depth + 4) * 4);
|
||||
_init_box(qdef->pool, ((qdef->item_sz+3)/4)*(qdef->depth) + 3, qdef->item_sz);
|
||||
return qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
void* buf;
|
||||
os_mbx_wait(qhdl->mbox, &buf, msec2wait(msec));
|
||||
memcpy(data, buf, qhdl->item_sz);
|
||||
_free_box(qhdl->pool, buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
(void) qhdl;
|
||||
return true; // nothing to do ?
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr) {
|
||||
void* buf = _alloc_box(qhdl->pool);
|
||||
memcpy(buf, data, qhdl->item_sz);
|
||||
if ( !in_isr ) {
|
||||
os_mbx_send(qhdl->mbox, buf, 0xFFFF);
|
||||
} else {
|
||||
isr_mbx_send(qhdl->mbox, buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return os_mbx_check(qhdl->mbox) == qhdl->depth;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
152
managed_components/espressif__tinyusb/src/osal/osal_zephyr.h
Normal file
152
managed_components/espressif__tinyusb/src/osal/osal_zephyr.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2025 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_OSAL_ZEPHYR_H
|
||||
#define TUSB_OSAL_ZEPHYR_H
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
k_msleep(msec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Spinlock API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
struct k_spinlock lock;
|
||||
k_spinlock_key_t key;
|
||||
} osal_spinlock_t;
|
||||
|
||||
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
|
||||
osal_spinlock_t _name
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
|
||||
(void) ctx;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
ctx->key = k_spin_lock(&ctx->lock);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
|
||||
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
|
||||
return; // single core MCU does not need to lock in ISR
|
||||
}
|
||||
k_spin_unlock(&ctx->lock, ctx->key);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct k_sem osal_semaphore_def_t, * osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
k_sem_init(semdef, 0, 255);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
k_sem_give(sem_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return 0 == k_sem_take(sem_hdl, K_MSEC(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
k_sem_reset(sem_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct k_mutex osal_mutex_def_t, *osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
if ( 0 == k_mutex_init(mdef) ) {
|
||||
return mdef;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return 0 == k_mutex_lock(mutex_hdl, K_MSEC(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return 0 == k_mutex_unlock(mutex_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct k_msgq osal_queue_def_t, * osal_queue_t;
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) K_MSGQ_DEFINE(_name, sizeof(_type), _depth, 4)
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
// K_MSGQ_DEFINE already initializes the queue
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
(void) qhdl;
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
return 0 == k_msgq_get(qhdl, data, K_MSEC(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
|
||||
return 0 == k_msgq_put(qhdl, data, in_isr ? K_NO_WAIT : K_FOREVER);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return 0 == k_msgq_num_used_get(qhdl);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2025 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_HCD_MAX3421_H
|
||||
#define TUSB_HCD_MAX3421_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// SPI transfer API with MAX3421E are implemented by application
|
||||
// - spi_cs_api(), spi_xfer_api(), int_api()
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// API to control MAX3421 SPI CS
|
||||
extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
|
||||
|
||||
// API to transfer data with MAX3421 SPI
|
||||
// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
|
||||
extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
|
||||
|
||||
// API to enable/disable MAX3421 INTR pin interrupt
|
||||
extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// API for read/write MAX3421 registers
|
||||
// are implemented by this driver, can be used by application
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// API to read MAX3421's register. Implemented by TinyUSB
|
||||
uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
|
||||
|
||||
// API to write MAX3421's register. Implemented by TinyUSB
|
||||
bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 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 _CI_FS_KINETIS_H
|
||||
#define _CI_FS_KINETIS_H
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
//static const ci_fs_controller_t _ci_controller[] = {
|
||||
// {.reg_base = USB0_BASE, .irqnum = USB0_IRQn}
|
||||
//};
|
||||
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
|
||||
#define CI_REG CI_FS_REG(0)
|
||||
|
||||
void dcd_int_enable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(USB0_IRQn);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(USB0_IRQn);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 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 _CI_FS_MCX_H
|
||||
#define _CI_FS_MCX_H
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MCXN9
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE)
|
||||
#define CIFS_IRQN USB0_FS_IRQn
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_MCXA15
|
||||
#define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
|
||||
#define CIFS_IRQN USB0_IRQn
|
||||
|
||||
#else
|
||||
#error "MCU is not supported"
|
||||
#endif
|
||||
|
||||
#define CI_REG CI_FS_REG(0)
|
||||
|
||||
void dcd_int_enable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_EnableIRQ(CIFS_IRQN);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
NVIC_DisableIRQ(CIFS_IRQN);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 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 _CI_FS_TYPE_H
|
||||
#define _CI_FS_TYPE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Note: some MCUs can only access these registers in 8-bit mode
|
||||
// align 4 is used to get rid of reserved fields
|
||||
#define _va32 volatile TU_ATTR_ALIGNED(4)
|
||||
|
||||
typedef struct {
|
||||
_va32 uint8_t PER_ID; // [00] Peripheral ID register
|
||||
_va32 uint8_t ID_COMP; // [04] Peripheral ID complement register
|
||||
_va32 uint8_t REV; // [08] Peripheral revision register
|
||||
_va32 uint8_t ADD_INFO; // [0C] Peripheral additional info register
|
||||
_va32 uint8_t OTG_ISTAT; // [10] OTG Interrupt Status Register
|
||||
_va32 uint8_t OTG_ICTRL; // [14] OTG Interrupt Control Register
|
||||
_va32 uint8_t OTG_STAT; // [18] OTG Status Register
|
||||
_va32 uint8_t OTG_CTRL; // [1C] OTG Control register
|
||||
uint32_t reserved_20[24]; // [20]
|
||||
_va32 uint8_t INT_STAT; // [80] Interrupt status register
|
||||
_va32 uint8_t INT_EN; // [84] Interrupt enable register
|
||||
_va32 uint8_t ERR_STAT; // [88] Error interrupt status register
|
||||
_va32 uint8_t ERR_ENB; // [8C] Error interrupt enable register
|
||||
_va32 uint8_t STAT; // [90] Status register
|
||||
_va32 uint8_t CTL; // [94] Control register
|
||||
_va32 uint8_t ADDR; // [98] Address register
|
||||
_va32 uint8_t BDT_PAGE1; // [9C] BDT page register 1
|
||||
_va32 uint8_t FRM_NUML; // [A0] Frame number register
|
||||
_va32 uint8_t FRM_NUMH; // [A4] Frame number register
|
||||
_va32 uint8_t TOKEN; // [A8] Token register
|
||||
_va32 uint8_t SOF_THLD; // [AC] SOF threshold register
|
||||
_va32 uint8_t BDT_PAGE2; // [B0] BDT page register 2
|
||||
_va32 uint8_t BDT_PAGE3; // [B4] BDT page register 3
|
||||
|
||||
uint32_t reserved_b8; // [B8]
|
||||
uint32_t reserved_bc; // [BC]
|
||||
|
||||
struct {
|
||||
_va32 uint8_t CTL;
|
||||
}EP[16]; // [C0] Endpoint control register
|
||||
|
||||
//----- Following is only found available in NXP Kinetis
|
||||
_va32 uint8_t USBCTRL; // [100] USB Control register,
|
||||
_va32 uint8_t OBSERVE; // [104] USB OTG Observe register,
|
||||
_va32 uint8_t CONTROL; // [108] USB OTG Control register,
|
||||
_va32 uint8_t USBTRC0; // [10C] USB Transceiver Control Register 0,
|
||||
uint32_t reserved_110; // [110]
|
||||
_va32 uint8_t USBFRMADJUST; // [114] Frame Adjust Register,
|
||||
|
||||
//----- Following is only found available in NXP MCX
|
||||
uint32_t reserved_118[3]; // [118]
|
||||
_va32 uint8_t KEEP_ALIVE_CTRL; // [124] Keep Alive Mode Control,
|
||||
_va32 uint8_t KEEP_ALIVE_WKCTRL; // [128] Keep Alive Mode Wakeup Control,
|
||||
_va32 uint8_t MISCCTRL; // [12C] Miscellaneous Control,
|
||||
_va32 uint8_t STALL_IL_DIS; // [130] Peripheral Mode Stall Disable for Endpoints[ 7..0] IN
|
||||
_va32 uint8_t STALL_IH_DIS; // [134] Peripheral Mode Stall Disable for Endpoints[15..8] IN
|
||||
_va32 uint8_t STALL_OL_DIS; // [138] Peripheral Mode Stall Disable for Endpoints[ 7..0] OUT
|
||||
_va32 uint8_t STALL_OH_DIS; // [13C] Peripheral Mode Stall Disable for Endpoints[15..8] OUT
|
||||
_va32 uint8_t CLK_RECOVER_CTRL; // [140] USB Clock Recovery Control,
|
||||
_va32 uint8_t CLK_RECOVER_IRC_EN; // [144] FIRC Oscillator Enable,
|
||||
uint32_t reserved_148[3]; // [148]
|
||||
_va32 uint8_t CLK_RECOVER_INT_EN; // [154] Clock Recovery Combined Interrupt Enable,
|
||||
uint32_t reserved_158; // [158]
|
||||
_va32 uint8_t CLK_RECOVER_INT_STATUS; // [15C] Clock Recovery Separated Interrupt Status,
|
||||
} ci_fs_regs_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(ci_fs_regs_t) == 0x160, "Size is not correct");
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t reg_base;
|
||||
uint32_t irqnum;
|
||||
} ci_fs_controller_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Koji Kitayama
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
|
||||
|
||||
#include "device/dcd.h"
|
||||
#include "ci_fs_type.h"
|
||||
|
||||
#if defined(TUP_USBIP_CHIPIDEA_FS_KINETIS)
|
||||
#include "ci_fs_kinetis.h"
|
||||
#elif defined(TUP_USBIP_CHIPIDEA_FS_MCX)
|
||||
#include "ci_fs_mcx.h"
|
||||
#else
|
||||
#error "MCU is not supported"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
TOK_PID_OUT = 0x1u,
|
||||
TOK_PID_IN = 0x9u,
|
||||
TOK_PID_SETUP = 0xDu,
|
||||
};
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
union {
|
||||
uint32_t head;
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
uint16_t : 2;
|
||||
__IO uint16_t tok_pid : 4;
|
||||
uint16_t data : 1;
|
||||
__IO uint16_t own : 1;
|
||||
uint16_t : 8;
|
||||
};
|
||||
struct {
|
||||
uint16_t : 2;
|
||||
uint16_t bdt_stall : 1;
|
||||
uint16_t dts : 1;
|
||||
uint16_t ninc : 1;
|
||||
uint16_t keep : 1;
|
||||
uint16_t : 10;
|
||||
};
|
||||
};
|
||||
__IO uint16_t bc : 10;
|
||||
uint16_t : 6;
|
||||
};
|
||||
};
|
||||
uint8_t *addr;
|
||||
}buffer_descriptor_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
union {
|
||||
uint32_t state;
|
||||
struct {
|
||||
uint32_t max_packet_size :11;
|
||||
uint32_t : 5;
|
||||
uint32_t odd : 1;
|
||||
uint32_t :15;
|
||||
};
|
||||
};
|
||||
uint16_t length;
|
||||
uint16_t remaining;
|
||||
}endpoint_state_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
/* [#EP][OUT,IN][EVEN,ODD] */
|
||||
buffer_descriptor_t bdt[16][2][2];
|
||||
uint16_t bda[512];
|
||||
};
|
||||
TU_ATTR_ALIGNED(4) union {
|
||||
endpoint_state_t endpoint[16][2];
|
||||
endpoint_state_t endpoint_unified[16 * 2];
|
||||
};
|
||||
uint8_t setup_packet[8];
|
||||
uint8_t addr;
|
||||
}dcd_data_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
// BDT(Buffer Descriptor Table) must be 256-byte aligned
|
||||
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
|
||||
|
||||
static void prepare_next_setup_packet(uint8_t rhport)
|
||||
{
|
||||
const unsigned out_odd = _dcd.endpoint[0][0].odd;
|
||||
const unsigned in_odd = _dcd.endpoint[0][1].odd;
|
||||
TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
|
||||
|
||||
_dcd.bdt[0][0][out_odd].data = 0;
|
||||
_dcd.bdt[0][0][out_odd ^ 1].data = 1;
|
||||
_dcd.bdt[0][1][in_odd].data = 1;
|
||||
_dcd.bdt[0][1][in_odd ^ 1].data = 0;
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
|
||||
_dcd.setup_packet, sizeof(_dcd.setup_packet));
|
||||
}
|
||||
|
||||
static void process_stall(uint8_t rhport)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint32_t const ep_ctl = CI_REG->EP[i].CTL;
|
||||
|
||||
if (ep_ctl & USB_ENDPT_EPSTALL_MASK) {
|
||||
// prepare next setup if endpoint0
|
||||
if ( i == 0 ) prepare_next_setup_packet(rhport);
|
||||
|
||||
// clear stall bit
|
||||
CI_REG->EP[i].CTL = ep_ctl & ~USB_ENDPT_EPSTALL_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_tokdne(uint8_t rhport)
|
||||
{
|
||||
const unsigned s = CI_REG->STAT;
|
||||
CI_REG->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
|
||||
|
||||
uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT);
|
||||
uint8_t const dir = (s & USB_STAT_TX_MASK) >> USB_STAT_TX_SHIFT;
|
||||
unsigned const odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
|
||||
|
||||
buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
|
||||
endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
|
||||
|
||||
/* fetch pid before discarded by the next steps */
|
||||
const unsigned pid = bd->tok_pid;
|
||||
|
||||
/* reset values for a next transfer */
|
||||
bd->bdt_stall = 0;
|
||||
bd->dts = 1;
|
||||
bd->ninc = 0;
|
||||
bd->keep = 0;
|
||||
/* update the odd variable to prepare for the next transfer */
|
||||
ep->odd = odd ^ 1;
|
||||
if (pid == TOK_PID_SETUP) {
|
||||
dcd_event_setup_received(rhport, bd->addr, true);
|
||||
CI_REG->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned bc = bd->bc;
|
||||
const unsigned remaining = ep->remaining - bc;
|
||||
if (remaining && bc == ep->max_packet_size) {
|
||||
/* continue the transferring consecutive data */
|
||||
ep->remaining = remaining;
|
||||
const int next_remaining = remaining - ep->max_packet_size;
|
||||
if (next_remaining > 0) {
|
||||
/* prepare to the after next transfer */
|
||||
bd->addr += ep->max_packet_size * 2;
|
||||
bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
|
||||
__DSB();
|
||||
bd->own = 1; /* the own bit must set after addr */
|
||||
}
|
||||
return;
|
||||
}
|
||||
const unsigned length = ep->length;
|
||||
dcd_event_xfer_complete(rhport,
|
||||
tu_edpt_addr(epnum, dir),
|
||||
length - remaining, XFER_RESULT_SUCCESS, true);
|
||||
if (0 == epnum && 0 == length) {
|
||||
/* After completion a ZLP of control transfer,
|
||||
* it prepares for the next steup transfer. */
|
||||
if (_dcd.addr) {
|
||||
/* When the transfer was the SetAddress,
|
||||
* the device address should be updated here. */
|
||||
CI_REG->ADDR = _dcd.addr;
|
||||
_dcd.addr = 0;
|
||||
}
|
||||
prepare_next_setup_packet(rhport);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_bus_reset(uint8_t rhport)
|
||||
{
|
||||
CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
|
||||
CI_REG->CTL |= USB_CTL_ODDRST_MASK;
|
||||
CI_REG->ADDR = 0;
|
||||
CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK |
|
||||
USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
|
||||
|
||||
CI_REG->EP[0].CTL = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
|
||||
for (unsigned i = 1; i < 16; ++i) {
|
||||
CI_REG->EP[i].CTL = 0;
|
||||
}
|
||||
buffer_descriptor_t *bd = _dcd.bdt[0][0];
|
||||
for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
|
||||
bd->head = 0;
|
||||
}
|
||||
const endpoint_state_t ep0 = {
|
||||
.max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.odd = 0,
|
||||
.length = 0,
|
||||
.remaining = 0,
|
||||
};
|
||||
_dcd.endpoint[0][0] = ep0;
|
||||
_dcd.endpoint[0][1] = ep0;
|
||||
tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
|
||||
_dcd.addr = 0;
|
||||
prepare_next_setup_packet(rhport);
|
||||
CI_REG->CTL &= ~USB_CTL_ODDRST_MASK;
|
||||
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
|
||||
}
|
||||
|
||||
static void process_bus_sleep(uint8_t rhport)
|
||||
{
|
||||
// Enable resume & disable suspend interrupt
|
||||
const unsigned inten = CI_REG->INT_EN;
|
||||
|
||||
CI_REG->INT_EN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
|
||||
CI_REG->USBTRC0 |= USB_USBTRC0_USBRESMEN_MASK;
|
||||
CI_REG->USBCTRL |= USB_USBCTRL_SUSP_MASK;
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
|
||||
static void process_bus_resume(uint8_t rhport)
|
||||
{
|
||||
// Enable suspend & disable resume interrupt
|
||||
const unsigned inten = CI_REG->INT_EN;
|
||||
|
||||
CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; // will also clear USB_USBTRC0_USB_RESUME_INT_MASK
|
||||
CI_REG->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
|
||||
CI_REG->INT_EN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
|
||||
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Device API
|
||||
*------------------------------------------------------------------*/
|
||||
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
(void) rhport;
|
||||
(void) rh_init;
|
||||
|
||||
// save crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
uint32_t clk_recover_irc_en = CI_REG->CLK_RECOVER_IRC_EN;
|
||||
uint32_t clk_recover_ctrl = CI_REG->CLK_RECOVER_CTRL;
|
||||
#endif
|
||||
|
||||
CI_REG->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
|
||||
while (CI_REG->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
|
||||
|
||||
// restore crystal-less setting (if available)
|
||||
#if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
|
||||
CI_REG->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
|
||||
CI_REG->CLK_RECOVER_CTRL |= clk_recover_ctrl;
|
||||
#endif
|
||||
|
||||
tu_memclr(&_dcd, sizeof(_dcd));
|
||||
CI_REG->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
|
||||
CI_REG->BDT_PAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
|
||||
CI_REG->BDT_PAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
|
||||
CI_REG->BDT_PAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
|
||||
|
||||
CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK;
|
||||
|
||||
dcd_connect(rhport);
|
||||
// NVIC_ClearPendingIRQ(CIFS_IRQN);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
||||
{
|
||||
_dcd.addr = dev_addr & 0x7F;
|
||||
/* Response with status first before changing device address */
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
CI_REG->CTL |= USB_CTL_RESUME_MASK;
|
||||
|
||||
unsigned cnt = SystemCoreClock / 1000;
|
||||
while (cnt--) __NOP();
|
||||
|
||||
CI_REG->CTL &= ~USB_CTL_RESUME_MASK;
|
||||
}
|
||||
|
||||
void dcd_connect(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
CI_REG->USBCTRL = 0;
|
||||
CI_REG->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
|
||||
CI_REG->CTL |= USB_CTL_USBENSOFEN_MASK;
|
||||
}
|
||||
|
||||
void dcd_disconnect(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
CI_REG->CTL = 0;
|
||||
CI_REG->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) en;
|
||||
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
const unsigned ep_addr = ep_desc->bEndpointAddress;
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned xfer = ep_desc->bmAttributes.xfer;
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
const unsigned odd = ep->odd;
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
|
||||
/* No support for control transfer */
|
||||
TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
|
||||
|
||||
ep->max_packet_size = tu_edpt_packet_size(ep_desc);
|
||||
unsigned val = USB_ENDPT_EPCTLDIS_MASK;
|
||||
val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
|
||||
val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
|
||||
CI_REG->EP[epn].CTL |= val;
|
||||
|
||||
if (xfer != TUSB_XFER_ISOCHRONOUS) {
|
||||
bd[odd].dts = 1;
|
||||
bd[odd].data = 0;
|
||||
bd[odd ^ 1].dts = 1;
|
||||
bd[odd ^ 1].data = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all(uint8_t rhport)
|
||||
{
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
for (unsigned i = 1; i < 16; ++i) {
|
||||
CI_REG->EP[i].CTL = 0;
|
||||
}
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
buffer_descriptor_t *bd = _dcd.bdt[1][0];
|
||||
for (unsigned i = 2; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
|
||||
bd->head = 0;
|
||||
}
|
||||
|
||||
endpoint_state_t *ep = &_dcd.endpoint[1][0];
|
||||
for (unsigned i = 2; i < sizeof(_dcd.endpoint)/sizeof(*ep); ++i, ++ep) {
|
||||
/* Clear except the odd */
|
||||
ep->max_packet_size = 0;
|
||||
ep->length = 0;
|
||||
ep->remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
CI_REG->EP[epn].CTL &= ~msk;
|
||||
ep->max_packet_size = 0;
|
||||
ep->length = 0;
|
||||
ep->remaining = 0;
|
||||
bd[0].head = 0;
|
||||
bd[1].head = 0;
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
|
||||
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
|
||||
TU_ASSERT(0 == bd->own);
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
ep->length = total_bytes;
|
||||
ep->remaining = total_bytes;
|
||||
|
||||
const unsigned mps = ep->max_packet_size;
|
||||
if (total_bytes > mps) {
|
||||
buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
|
||||
/* When total_bytes is greater than the max packet size,
|
||||
* it prepares to the next transfer to avoid NAK in advance. */
|
||||
next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps;
|
||||
next->addr = buffer + mps;
|
||||
next->own = 1;
|
||||
}
|
||||
bd->bc = total_bytes >= mps ? mps: total_bytes;
|
||||
bd->addr = buffer;
|
||||
__DSB();
|
||||
bd->own = 1; /* This bit must be set last */
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
(void) rhport;
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
|
||||
if (0 == epn) {
|
||||
CI_REG->EP[epn].CTL |= USB_ENDPT_EPSTALL_MASK;
|
||||
} else {
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned odd = _dcd.endpoint[epn][dir].odd;
|
||||
buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][odd];
|
||||
TU_ASSERT(0 == bd->own,);
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
bd->bdt_stall = 1;
|
||||
__DSB();
|
||||
bd->own = 1; /* This bit must be set last */
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
TU_VERIFY(epn,);
|
||||
const unsigned dir = tu_edpt_dir(ep_addr);
|
||||
const unsigned odd = _dcd.endpoint[epn][dir].odd;
|
||||
buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
|
||||
TU_VERIFY(bd[odd].own,);
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
bd[odd].own = 0;
|
||||
__DSB();
|
||||
|
||||
// clear stall
|
||||
bd[odd].bdt_stall = 0;
|
||||
|
||||
// Reset data toggle
|
||||
bd[odd ].data = 0;
|
||||
bd[odd ^ 1].data = 1;
|
||||
|
||||
// We already cleared this in ISR, but just clear it here to be safe
|
||||
const uint32_t ep_ctl = CI_REG->EP[epn].CTL;
|
||||
if (ep_ctl & USB_ENDPT_EPSTALL_MASK) {
|
||||
CI_REG->EP[epn].CTL = ep_ctl & ~USB_ENDPT_EPSTALL_MASK;
|
||||
}
|
||||
|
||||
dcd_int_enable(rhport);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ISR
|
||||
//--------------------------------------------------------------------+
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
uint32_t is = CI_REG->INT_STAT;
|
||||
uint32_t msk = CI_REG->INT_EN;
|
||||
|
||||
// clear non-enabled interrupts
|
||||
CI_REG->INT_STAT = is & ~msk;
|
||||
is &= msk;
|
||||
|
||||
if (is & USB_ISTAT_ERROR_MASK) {
|
||||
/* TODO: */
|
||||
uint32_t es = CI_REG->ERR_STAT;
|
||||
CI_REG->ERR_STAT = es;
|
||||
CI_REG->INT_STAT = is; /* discard any pending events */
|
||||
}
|
||||
|
||||
if (is & USB_ISTAT_USBRST_MASK) {
|
||||
CI_REG->INT_STAT = is; /* discard any pending events */
|
||||
process_bus_reset(rhport);
|
||||
}
|
||||
|
||||
if (is & USB_ISTAT_SLEEP_MASK) {
|
||||
// TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
|
||||
|
||||
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
|
||||
// detected as Sleep event. Though usbd has debouncing logic so we are good
|
||||
CI_REG->INT_STAT = USB_ISTAT_SLEEP_MASK;
|
||||
process_bus_sleep(rhport);
|
||||
}
|
||||
|
||||
#if 0 // ISTAT_RESUME never trigger, probably for host mode ?
|
||||
if (is & USB_ISTAT_RESUME_MASK) {
|
||||
// TU_LOG2("ISTAT Resume: "); TU_LOG2_HEX(is);
|
||||
KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
|
||||
process_bus_resume(rhport);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CI_REG->USBTRC0 & USB_USBTRC0_USB_RESUME_INT_MASK) {
|
||||
// TU_LOG2("USBTRC0 Resume: "); TU_LOG2_HEX(is); TU_LOG2_HEX(KHCI->USBTRC0);
|
||||
process_bus_resume(rhport);
|
||||
}
|
||||
|
||||
if (is & USB_ISTAT_SOFTOK_MASK) {
|
||||
CI_REG->INT_STAT = USB_ISTAT_SOFTOK_MASK;
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
|
||||
}
|
||||
|
||||
if (is & USB_ISTAT_STALL_MASK) {
|
||||
CI_REG->INT_STAT = USB_ISTAT_STALL_MASK;
|
||||
process_stall(rhport);
|
||||
}
|
||||
|
||||
if (is & USB_ISTAT_TOKDNE_MASK) {
|
||||
process_tokdne(rhport);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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 _CI_HS_IMXRT_H_
|
||||
#define _CI_HS_IMXRT_H_
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#if !defined(USB1_BASE) && defined(USB_OTG1_BASE)
|
||||
#define USB1_BASE USB_OTG1_BASE
|
||||
#endif
|
||||
|
||||
#if !defined(USB2_BASE) && defined(USB_OTG2_BASE)
|
||||
#define USB2_BASE USB_OTG2_BASE
|
||||
#endif
|
||||
|
||||
// RT1040 calls its only USB USB_OTG (no 1)
|
||||
#if defined(MIMXRT1042_SERIES)
|
||||
#define USB_OTG1_IRQn USB_OTG_IRQn
|
||||
#endif
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] =
|
||||
{
|
||||
// RT1010 and RT1020 only has 1 USB controller
|
||||
#if FSL_FEATURE_SOC_USBHS_COUNT == 1
|
||||
{ .reg_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
|
||||
#else
|
||||
{ .reg_base = USB1_BASE, .irqnum = USB_OTG1_IRQn},
|
||||
{ .reg_base = USB2_BASE, .irqnum = USB_OTG2_IRQn}
|
||||
#endif
|
||||
};
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
//------------- DCD -------------//
|
||||
#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
|
||||
#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
|
||||
|
||||
//------------- HCD -------------//
|
||||
#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
|
||||
#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
|
||||
|
||||
//------------- DCache -------------//
|
||||
#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
|
||||
#if __CORTEX_M == 7
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
|
||||
if (size & (CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) {
|
||||
size = (size & ~(CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) + CFG_TUD_MEM_DCACHE_LINE_SIZE;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_is_cache_mem(uintptr_t addr) {
|
||||
return !(0x20000000 <= addr && addr < 0x20100000);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean(void const* addr, uint32_t data_size) {
|
||||
const uintptr_t addr32 = (uintptr_t) addr;
|
||||
if (imxrt_is_cache_mem(addr32)) {
|
||||
TU_ASSERT(tu_is_aligned32(addr32));
|
||||
data_size = round_up_to_cache_line_size(data_size);
|
||||
SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_invalidate(void const* addr, uint32_t data_size) {
|
||||
const uintptr_t addr32 = (uintptr_t) addr;
|
||||
if (imxrt_is_cache_mem(addr32)) {
|
||||
// Invalidating does not push cached changes back to RAM so we need to be
|
||||
// *very* careful when we do it. If we're not aligned, then we risk resetting
|
||||
// values back to their RAM state.
|
||||
TU_ASSERT(tu_is_aligned32(addr32));
|
||||
data_size = round_up_to_cache_line_size(data_size);
|
||||
SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
|
||||
const uintptr_t addr32 = (uintptr_t) addr;
|
||||
if (imxrt_is_cache_mem(addr32)) {
|
||||
TU_ASSERT(tu_is_aligned32(addr32));
|
||||
data_size = round_up_to_cache_line_size(data_size);
|
||||
SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif __CORTEX_M == 4
|
||||
#error "Secondary M4 core's cache controller is not supported yet."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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 _CI_HS_LPC18_43_H_
|
||||
#define _CI_HS_LPC18_43_H_
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
// LPCOpen for 18xx & 43xx
|
||||
#include "chip.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] =
|
||||
{
|
||||
{ .reg_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
|
||||
{ .reg_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
|
||||
};
|
||||
|
||||
#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
|
||||
|
||||
#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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 _CI_HS_MCX_H_
|
||||
#define _CI_HS_MCX_H_
|
||||
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
// NOTE: MCX N9 has 2 different USB Controller
|
||||
// - USB0 is KHCI FullSpeed
|
||||
// - USB1 is ChipIdea HighSpeed, therefore rhport = 1 is actually index 0
|
||||
|
||||
static const ci_hs_controller_t _ci_controller[] = {
|
||||
{.reg_base = USBHS1__USBC_BASE, .irqnum = USB1_HS_IRQn}
|
||||
};
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline ci_hs_regs_t* CI_HS_REG(uint8_t port) {
|
||||
(void) port;
|
||||
return ((ci_hs_regs_t*) _ci_controller[0].reg_base);
|
||||
}
|
||||
|
||||
#define CI_DCD_INT_ENABLE(_p) do { (void) _p; NVIC_EnableIRQ (_ci_controller[0].irqnum); } while (0)
|
||||
#define CI_DCD_INT_DISABLE(_p) do { (void) _p; NVIC_DisableIRQ(_ci_controller[0].irqnum); } while (0)
|
||||
|
||||
#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
|
||||
#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, 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 CI_HS_TYPE_H_
|
||||
#define CI_HS_TYPE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// DCCPARAMS
|
||||
enum {
|
||||
DCCPARAMS_DEN_MASK = 0x1Fu, ///< DEN bit 4:0
|
||||
};
|
||||
|
||||
// USBCMD
|
||||
enum {
|
||||
USBCMD_RUN_STOP = TU_BIT(0),
|
||||
USBCMD_RESET = TU_BIT(1),
|
||||
USBCMD_SETUP_TRIPWIRE = TU_BIT(13),
|
||||
USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14), // This bit is used as a semaphore to ensure the to proper addition of a
|
||||
// new dTD to an active (primed) endpoint’s linked list. This bit is set and
|
||||
// cleared by software during the process of adding a new dTD
|
||||
|
||||
USBCMD_INTR_THRESHOLD_MASK = 0x00FF0000u, // Interrupt Threshold bit 23:16
|
||||
};
|
||||
|
||||
// PORTSC1
|
||||
#define PORTSC1_PORT_SPEED_POS 26
|
||||
|
||||
enum {
|
||||
PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0),
|
||||
PORTSC1_FORCE_PORT_RESUME = TU_BIT(6),
|
||||
PORTSC1_SUSPEND = TU_BIT(7),
|
||||
PORTSC1_FORCE_FULL_SPEED = TU_BIT(24),
|
||||
PORTSC1_PORT_SPEED = TU_BIT(26) | TU_BIT(27)
|
||||
};
|
||||
|
||||
// OTGSC
|
||||
enum {
|
||||
OTGSC_VBUS_DISCHARGE = TU_BIT(0),
|
||||
OTGSC_VBUS_CHARGE = TU_BIT(1),
|
||||
// OTGSC_HWASSIST_AUTORESET = TU_BIT(2),
|
||||
OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode
|
||||
OTGSC_DATA_PULSING = TU_BIT(4),
|
||||
OTGSC_ID_PULLUP = TU_BIT(5),
|
||||
// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6),
|
||||
// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7),
|
||||
OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device
|
||||
OTGSC_A_VBUS_VALID = TU_BIT(9),
|
||||
OTGSC_A_SESSION_VALID = TU_BIT(10),
|
||||
OTGSC_B_SESSION_VALID = TU_BIT(11),
|
||||
OTGSC_B_SESSION_END = TU_BIT(12),
|
||||
OTGSC_1MS_TOGGLE = TU_BIT(13),
|
||||
OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14),
|
||||
};
|
||||
|
||||
// USBMode
|
||||
enum {
|
||||
USBMOD_CM_MASK = TU_BIT(0) | TU_BIT(1),
|
||||
USBMODE_CM_DEVICE = 2,
|
||||
USBMODE_CM_HOST = 3,
|
||||
|
||||
USBMODE_SLOM = TU_BIT(3),
|
||||
USBMODE_SDIS = TU_BIT(4),
|
||||
|
||||
USBMODE_VBUS_POWER_SELECT = TU_BIT(5), // Need to be enabled for LPC18XX/43XX in host mode
|
||||
};
|
||||
|
||||
// Device Registers
|
||||
typedef struct
|
||||
{
|
||||
//------------- ID + HW Parameter Registers-------------//
|
||||
volatile uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX
|
||||
|
||||
//------------- Capability Registers-------------//
|
||||
volatile uint8_t CAPLENGTH; ///< Capability Registers Length
|
||||
volatile uint8_t TU_RESERVED[1];
|
||||
volatile uint16_t HCIVERSION; ///< Host Controller Interface Version
|
||||
|
||||
volatile uint32_t HCSPARAMS; ///< Host Controller Structural Parameters
|
||||
volatile uint32_t HCCPARAMS; ///< Host Controller Capability Parameters
|
||||
volatile uint32_t TU_RESERVED[5];
|
||||
|
||||
volatile uint16_t DCIVERSION; ///< Device Controller Interface Version
|
||||
volatile uint8_t TU_RESERVED[2];
|
||||
|
||||
volatile uint32_t DCCPARAMS; ///< Device Controller Capability Parameters
|
||||
volatile uint32_t TU_RESERVED[6];
|
||||
|
||||
//------------- Operational Registers -------------//
|
||||
volatile uint32_t USBCMD; ///< USB Command Register
|
||||
volatile uint32_t USBSTS; ///< USB Status Register
|
||||
volatile uint32_t USBINTR; ///< Interrupt Enable Register
|
||||
volatile uint32_t FRINDEX; ///< USB Frame Index
|
||||
volatile uint32_t TU_RESERVED;
|
||||
volatile uint32_t DEVICEADDR; ///< Device Address
|
||||
volatile uint32_t ENDPTLISTADDR; ///< Endpoint List Address
|
||||
volatile uint32_t TU_RESERVED;
|
||||
volatile uint32_t BURSTSIZE; ///< Programmable Burst Size
|
||||
volatile uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning
|
||||
uint32_t TU_RESERVED[4];
|
||||
volatile uint32_t ENDPTNAK; ///< Endpoint NAK
|
||||
volatile uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable
|
||||
volatile uint32_t TU_RESERVED;
|
||||
volatile uint32_t PORTSC1; ///< Port Status & Control
|
||||
volatile uint32_t TU_RESERVED[7];
|
||||
volatile uint32_t OTGSC; ///< On-The-Go Status & control
|
||||
volatile uint32_t USBMODE; ///< USB Device Mode
|
||||
volatile uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status
|
||||
volatile uint32_t ENDPTPRIME; ///< Endpoint Prime
|
||||
volatile uint32_t ENDPTFLUSH; ///< Endpoint Flush
|
||||
volatile uint32_t ENDPTSTAT; ///< Endpoint Status
|
||||
volatile uint32_t ENDPTCOMPLETE; ///< Endpoint Complete
|
||||
volatile uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7
|
||||
} ci_hs_regs_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t reg_base;
|
||||
uint32_t irqnum;
|
||||
}ci_hs_controller_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CI_HS_TYPE_H_ */
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_HS)
|
||||
|
||||
#include "device/dcd.h"
|
||||
#include "ci_hs_type.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
|
||||
#include "ci_hs_imxrt.h"
|
||||
|
||||
#if CFG_TUD_MEM_DCACHE_ENABLE
|
||||
bool dcd_dcache_clean(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_clean(addr, data_size);
|
||||
}
|
||||
|
||||
bool dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_clean_invalidate(addr, data_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
|
||||
// MCX N9 only port 1 use this controller
|
||||
#include "ci_hs_mcx.h"
|
||||
|
||||
#else
|
||||
#error "Unsupported MCUs"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// ENDPTCTRL
|
||||
enum {
|
||||
ENDPTCTRL_STALL = TU_BIT(0),
|
||||
ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only
|
||||
ENDPTCTRL_TOGGLE_RESET = TU_BIT(6),
|
||||
ENDPTCTRL_ENABLE = TU_BIT(7)
|
||||
};
|
||||
|
||||
enum {
|
||||
ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field
|
||||
};
|
||||
|
||||
// USBSTS, USBINTR
|
||||
enum {
|
||||
INTR_USB = TU_BIT(0),
|
||||
INTR_ERROR = TU_BIT(1),
|
||||
INTR_PORT_CHANGE = TU_BIT(2),
|
||||
INTR_RESET = TU_BIT(6),
|
||||
INTR_SOF = TU_BIT(7),
|
||||
INTR_SUSPEND = TU_BIT(8),
|
||||
INTR_NAK = TU_BIT(16)
|
||||
};
|
||||
|
||||
// Queue Transfer Descriptor
|
||||
typedef struct
|
||||
{
|
||||
// Word 0: Next QTD Pointer
|
||||
uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
|
||||
|
||||
// Word 1: qTQ Token
|
||||
uint32_t : 3 ;
|
||||
volatile uint32_t xact_err : 1 ;
|
||||
uint32_t : 1 ;
|
||||
volatile uint32_t buffer_err : 1 ;
|
||||
volatile uint32_t halted : 1 ;
|
||||
volatile uint32_t active : 1 ;
|
||||
uint32_t : 2 ;
|
||||
uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
|
||||
uint32_t : 3 ;
|
||||
uint32_t int_on_complete : 1 ;
|
||||
volatile uint32_t total_bytes : 15 ;
|
||||
uint32_t : 1 ;
|
||||
|
||||
// Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
|
||||
uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TD is 32 bytes aligned but occupies only 28 bytes
|
||||
// Therefore there are 4 bytes padding that we can use.
|
||||
//--------------------------------------------------------------------+
|
||||
uint16_t expected_bytes;
|
||||
uint8_t reserved[2];
|
||||
} dcd_qtd_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
|
||||
|
||||
// Queue Head
|
||||
typedef struct
|
||||
{
|
||||
// Word 0: Capabilities and Characteristics
|
||||
uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
|
||||
uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
|
||||
uint32_t max_packet_size : 11 ; ///< Endpoint's wMaxPacketSize
|
||||
uint32_t : 2 ;
|
||||
uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
|
||||
uint32_t iso_mult : 2 ; ///<
|
||||
|
||||
// Word 1: Current qTD Pointer
|
||||
volatile uint32_t qtd_addr;
|
||||
|
||||
// Word 2-9: Transfer Overlay
|
||||
volatile dcd_qtd_t qtd_overlay;
|
||||
|
||||
// Word 10-11: Setup request (control OUT only)
|
||||
volatile tusb_control_request_t setup_request;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QHD is 64 bytes aligned but occupies only 48 bytes
|
||||
// Therefore there are 16 bytes padding that we can use.
|
||||
//--------------------------------------------------------------------+
|
||||
tu_fifo_t * ff;
|
||||
uint8_t reserved[12];
|
||||
} dcd_qhd_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Variables
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define QTD_NEXT_INVALID 0x01
|
||||
|
||||
typedef struct {
|
||||
// Must be at 2K alignment
|
||||
// Each endpoint with direction (IN/OUT) occupies a queue head
|
||||
// for portability, TinyUSB only queue 1 TD for each Qhd
|
||||
dcd_qhd_t qhd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64);
|
||||
dcd_qtd_t qtd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
|
||||
}dcd_data_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(2048)
|
||||
static dcd_data_t _dcd_data;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Prototypes and Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline uint8_t ci_ep_count(ci_hs_regs_t const* dcd_reg)
|
||||
{
|
||||
return dcd_reg->DCCPARAMS & DCCPARAMS_DEN_MASK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// follows LPC43xx User Manual 23.10.3
|
||||
static void bus_reset(uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// The reset value for all endpoint types is the control endpoint. If one endpoint
|
||||
// direction is enabled and the paired endpoint of opposite direction is disabled, then the
|
||||
// endpoint type of the unused direction must be changed from the control type to any other
|
||||
// type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
|
||||
// for the data PID tracking on the active endpoint.
|
||||
uint8_t const ep_count = ci_ep_count(dcd_reg);
|
||||
for( uint8_t i=1; i < ep_count; i++)
|
||||
{
|
||||
dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
|
||||
}
|
||||
|
||||
//------------- Clear All Registers -------------//
|
||||
dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK;
|
||||
dcd_reg->ENDPTNAKEN = 0;
|
||||
dcd_reg->USBSTS = dcd_reg->USBSTS;
|
||||
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
|
||||
dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE;
|
||||
|
||||
while (dcd_reg->ENDPTPRIME) {}
|
||||
dcd_reg->ENDPTFLUSH = 0xFFFFFFFF;
|
||||
while (dcd_reg->ENDPTFLUSH) {}
|
||||
|
||||
// read reset bit in portsc
|
||||
|
||||
//------------- Queue Head & Queue TD -------------//
|
||||
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
//------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
|
||||
_dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1;
|
||||
_dcd_data.qhd[0][0].max_packet_size = _dcd_data.qhd[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
|
||||
_dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
|
||||
|
||||
_dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
|
||||
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
}
|
||||
|
||||
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
(void) rh_init;
|
||||
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX);
|
||||
|
||||
// Reset controller
|
||||
dcd_reg->USBCMD |= USBCMD_RESET;
|
||||
while( dcd_reg->USBCMD & USBCMD_RESET ) {}
|
||||
|
||||
// Set mode to device, must be set immediately after reset
|
||||
uint32_t usbmode = dcd_reg->USBMODE & ~USBMOD_CM_MASK;
|
||||
usbmode |= USBMODE_CM_DEVICE;
|
||||
dcd_reg->USBMODE = usbmode;
|
||||
|
||||
#ifdef CFG_TUD_CI_HS_VBUS_CHARGE
|
||||
dcd_reg->OTGSC = OTGSC_VBUS_CHARGE | OTGSC_OTG_TERMINATION;
|
||||
#else
|
||||
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
|
||||
#endif
|
||||
|
||||
#if !TUD_OPT_HIGH_SPEED
|
||||
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
|
||||
#endif
|
||||
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
|
||||
dcd_reg->USBSTS = dcd_reg->USBSTS;
|
||||
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
|
||||
|
||||
uint32_t usbcmd = dcd_reg->USBCMD;
|
||||
usbcmd &= ~USBCMD_INTR_THRESHOLD_MASK; // Interrupt Threshold Interval = 0
|
||||
usbcmd |= USBCMD_RUN_STOP; // run
|
||||
|
||||
dcd_reg->USBCMD = usbcmd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_int_enable(uint8_t rhport)
|
||||
{
|
||||
CI_DCD_INT_ENABLE(rhport);
|
||||
}
|
||||
|
||||
void dcd_int_disable(uint8_t rhport)
|
||||
{
|
||||
CI_DCD_INT_DISABLE(rhport);
|
||||
}
|
||||
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
|
||||
{
|
||||
// Response with status first before changing device address
|
||||
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME;
|
||||
}
|
||||
|
||||
void dcd_connect(uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->USBCMD |= USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_disconnect(uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
if (en) {
|
||||
dcd_reg->USBINTR |= INTR_SOF;
|
||||
} else {
|
||||
dcd_reg->USBINTR &= ~INTR_SOF;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
|
||||
{
|
||||
dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes);
|
||||
|
||||
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
|
||||
|
||||
p_qtd->next = QTD_NEXT_INVALID;
|
||||
p_qtd->active = 1;
|
||||
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
|
||||
p_qtd->int_on_complete = true;
|
||||
|
||||
if (data_ptr != NULL)
|
||||
{
|
||||
p_qtd->buffer[0] = (uint32_t) data_ptr;
|
||||
|
||||
uint32_t const bufend = p_qtd->buffer[0] + total_bytes;
|
||||
for(uint8_t i=1; i<5; i++)
|
||||
{
|
||||
uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096;
|
||||
if ( bufend <= next_page ) break;
|
||||
|
||||
p_qtd->buffer[i] = next_page;
|
||||
|
||||
// TODO page[1] FRAME_N for ISO transfer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DCD Endpoint Port
|
||||
//--------------------------------------------------------------------+
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
|
||||
|
||||
// flush to abort any primed buffer
|
||||
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
// data toggle also need to be reset
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 );
|
||||
dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0));
|
||||
}
|
||||
|
||||
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// Must not exceed max endpoint number
|
||||
TU_ASSERT(epnum < ci_ep_count(dcd_reg));
|
||||
|
||||
//------------- Prepare Queue Head -------------//
|
||||
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
tu_memclr(p_qhd, sizeof(dcd_qhd_t));
|
||||
|
||||
p_qhd->zero_length_termination = 1;
|
||||
p_qhd->max_packet_size = tu_edpt_packet_size(p_endpoint_desc);
|
||||
if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
|
||||
{
|
||||
p_qhd->iso_mult = 1;
|
||||
}
|
||||
|
||||
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
|
||||
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
// Enable EP Control
|
||||
uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
|
||||
|
||||
if ( dir == TUSB_DIR_OUT )
|
||||
{
|
||||
dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl;
|
||||
}else
|
||||
{
|
||||
dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// Disable all non-control endpoints
|
||||
uint8_t const ep_count = ci_ep_count(dcd_reg);
|
||||
for (uint8_t epnum = 1; epnum < ep_count; epnum++)
|
||||
{
|
||||
_dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
|
||||
_dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
|
||||
|
||||
dcd_reg->ENDPTFLUSH = TU_BIT(epnum) | TU_BIT(epnum+16);
|
||||
dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
_dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
|
||||
|
||||
// Flush EP
|
||||
uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
|
||||
dcd_reg->ENDPTFLUSH = flush_mask;
|
||||
while(dcd_reg->ENDPTFLUSH & flush_mask);
|
||||
|
||||
// Clear EP enable
|
||||
dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0));
|
||||
}
|
||||
|
||||
static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
|
||||
|
||||
p_qhd->qtd_overlay.halted = false; // clear any previous error
|
||||
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
|
||||
|
||||
// flush cache
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
if ( epnum == 0 )
|
||||
{
|
||||
// follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
|
||||
// wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
|
||||
while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
|
||||
}
|
||||
|
||||
// start transfer
|
||||
dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0));
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
|
||||
|
||||
// Prepare qtd
|
||||
qtd_init(p_qtd, buffer, total_bytes);
|
||||
|
||||
// Start qhd transfer
|
||||
p_qhd->ff = NULL;
|
||||
qhd_start_xfer(rhport, epnum, dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !CFG_TUD_MEM_DCACHE_ENABLE
|
||||
// fifo has to be aligned to 4k boundary
|
||||
// It's incompatible with dcache enabled transfer, since neither address nor size is aligned to cache line
|
||||
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
|
||||
{
|
||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
|
||||
|
||||
tu_fifo_buffer_info_t fifo_info;
|
||||
|
||||
if (dir)
|
||||
{
|
||||
tu_fifo_get_read_info(ff, &fifo_info);
|
||||
} else
|
||||
{
|
||||
tu_fifo_get_write_info(ff, &fifo_info);
|
||||
}
|
||||
|
||||
if ( fifo_info.len_lin >= total_bytes )
|
||||
{
|
||||
// Linear length is enough for this transfer
|
||||
qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// linear part is not enough
|
||||
|
||||
// prepare TD up to linear length
|
||||
qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin);
|
||||
|
||||
if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) )
|
||||
{
|
||||
// If buffer is aligned to 4K & buffer size is multiple of 4K
|
||||
// We can make use of buffer page array to also combine the linear + wrapped length
|
||||
p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
|
||||
|
||||
for(uint8_t i = 1, page = 0; i < 5; i++)
|
||||
{
|
||||
// pick up buffer array where linear ends
|
||||
if (p_qtd->buffer[i] == 0)
|
||||
{
|
||||
p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page;
|
||||
page++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO we may need to carry the wrapped length after the linear part complete
|
||||
// for now only transfer up to linear part
|
||||
}
|
||||
}
|
||||
|
||||
// Start qhd transfer
|
||||
p_qhd->ff = ff;
|
||||
qhd_start_xfer(rhport, epnum, dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ISR
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir)
|
||||
{
|
||||
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
|
||||
dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
|
||||
|
||||
uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
|
||||
( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
|
||||
|
||||
if ( result != XFER_RESULT_SUCCESS )
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
// flush to abort error buffer
|
||||
dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
|
||||
}
|
||||
|
||||
uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
|
||||
|
||||
if (p_qhd->ff)
|
||||
{
|
||||
if (dir == TUSB_DIR_IN)
|
||||
{
|
||||
tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes);
|
||||
} else
|
||||
{
|
||||
tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// only number of bytes in the IOC qtd
|
||||
dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
|
||||
}
|
||||
|
||||
void dcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
uint32_t const int_enable = dcd_reg->USBINTR;
|
||||
uint32_t const int_status = dcd_reg->USBSTS & int_enable;
|
||||
dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
|
||||
|
||||
// disabled interrupt sources
|
||||
if (int_status == 0) return;
|
||||
|
||||
// Set if the port controller enters the full or high-speed operational state.
|
||||
// either from Bus Reset or Suspended state
|
||||
if (int_status & INTR_PORT_CHANGE)
|
||||
{
|
||||
// TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
|
||||
|
||||
// Reset interrupt is not enabled, we manually check if Port Change is due
|
||||
// to connection / disconnection
|
||||
if ( dcd_reg->USBSTS & INTR_RESET )
|
||||
{
|
||||
dcd_reg->USBSTS = INTR_RESET;
|
||||
|
||||
if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS)
|
||||
{
|
||||
uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
|
||||
bus_reset(rhport);
|
||||
dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
|
||||
}else
|
||||
{
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Triggered by resuming from suspended state
|
||||
if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) )
|
||||
{
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (int_status & INTR_SUSPEND)
|
||||
{
|
||||
// TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1);
|
||||
|
||||
if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
|
||||
{
|
||||
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
|
||||
// Skip suspend event if we are not addressed
|
||||
if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
|
||||
{
|
||||
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (int_status & INTR_USB)
|
||||
{
|
||||
// Make sure we read the latest version of _dcd_data.
|
||||
dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
|
||||
|
||||
uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
|
||||
dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
|
||||
|
||||
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
|
||||
// nothing to do, we will submit xfer as error to usbd
|
||||
// if (int_status & INTR_ERROR) { }
|
||||
|
||||
if ( edpt_complete )
|
||||
{
|
||||
for(uint8_t epnum = 0; epnum < TUP_DCD_ENDPOINT_MAX; epnum++)
|
||||
{
|
||||
if ( tu_bit_test(edpt_complete, epnum) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
|
||||
if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up Received
|
||||
// 23.10.10.2 Operational model for setup transfers
|
||||
// Must be after normal transfer complete since it is possible to have both previous control status + new setup
|
||||
// in the same frame and we should handle previous status first.
|
||||
if (dcd_reg->ENDPTSETUPSTAT) {
|
||||
dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
|
||||
dcd_event_setup_received(rhport, (uint8_t *) (uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (int_status & INTR_SOF)
|
||||
{
|
||||
const uint32_t frame = dcd_reg->FRINDEX;
|
||||
dcd_event_sof(rhport, frame, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
// Chipidea Highspeed USB IP implement EHCI for host functionality
|
||||
|
||||
#if CFG_TUH_ENABLED && defined(TUP_USBIP_EHCI)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_common.h"
|
||||
#include "host/hcd.h"
|
||||
#include "host/usbh.h"
|
||||
#include "portable/ehci/ehci_api.h"
|
||||
#include "ci_hs_type.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
|
||||
|
||||
#include "ci_hs_imxrt.h"
|
||||
|
||||
#if CFG_TUH_MEM_DCACHE_ENABLE
|
||||
bool hcd_dcache_clean(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_clean(addr, data_size);
|
||||
}
|
||||
|
||||
bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_invalidate(addr, data_size);
|
||||
}
|
||||
|
||||
bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
|
||||
return imxrt_dcache_clean_invalidate(addr, data_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
|
||||
#include "ci_hs_lpc18_43.h"
|
||||
|
||||
#else
|
||||
#error "Unsupported MCUs"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
(void) rh_init;
|
||||
ci_hs_regs_t *hcd_reg = CI_HS_REG(rhport);
|
||||
|
||||
// Reset controller
|
||||
hcd_reg->USBCMD |= USBCMD_RESET;
|
||||
while ( hcd_reg->USBCMD & USBCMD_RESET ) {}
|
||||
|
||||
// Set mode to device, must be set immediately after reset
|
||||
#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
|
||||
// LPC18XX/43XX need to set VBUS Power Select to HIGH
|
||||
// RHPORT1 is fullspeed only (need external PHY for Highspeed)
|
||||
hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
|
||||
if (rhport == 1) {
|
||||
hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
|
||||
}
|
||||
#else
|
||||
hcd_reg->USBMODE = USBMODE_CM_HOST;
|
||||
#endif
|
||||
|
||||
return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
|
||||
}
|
||||
|
||||
void hcd_int_enable(uint8_t rhport) {
|
||||
CI_HCD_INT_ENABLE(rhport);
|
||||
}
|
||||
|
||||
void hcd_int_disable(uint8_t rhport) {
|
||||
CI_HCD_INT_DISABLE(rhport);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
1023
managed_components/espressif__tinyusb/src/portable/ehci/ehci.c
Normal file
1023
managed_components/espressif__tinyusb/src/portable/ehci/ehci.c
Normal file
File diff suppressed because it is too large
Load Diff
461
managed_components/espressif__tinyusb/src/portable/ehci/ehci.h
Normal file
461
managed_components/espressif__tinyusb/src/portable/ehci/ehci.h
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_EHCI_H_
|
||||
#define _TUSB_EHCI_H_
|
||||
|
||||
|
||||
/* Abbreviation
|
||||
* HC: Host Controller
|
||||
* HCD: Host Controller Driver
|
||||
* QHD: Queue Head for non-ISO transfer
|
||||
* QTD: Queue Transfer Descriptor for non-ISO transfer
|
||||
* ITD: Iso Transfer Descriptor for highspeed
|
||||
* SITD: Split ISO Transfer Descriptor for full-speed
|
||||
* SMASK: Start Split mask for Slipt Transaction
|
||||
* CMASK: Complete Split mask for Slipt Transaction
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// EHCI CONFIGURATION & CONSTANTS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TODO merge OHCI with EHCI
|
||||
enum {
|
||||
EHCI_MAX_ITD = 4,
|
||||
EHCI_MAX_SITD = 16
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// EHCI Data Structure
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
EHCI_QTYPE_ITD = 0 ,
|
||||
EHCI_QTYPE_QHD ,
|
||||
EHCI_QTYPE_SITD ,
|
||||
EHCI_QTYPE_FSTN
|
||||
};
|
||||
|
||||
/// EHCI PID
|
||||
enum {
|
||||
EHCI_PID_OUT = 0 ,
|
||||
EHCI_PID_IN ,
|
||||
EHCI_PID_SETUP
|
||||
};
|
||||
|
||||
/// Link pointer
|
||||
typedef union {
|
||||
uint32_t address;
|
||||
struct {
|
||||
uint32_t terminate : 1;
|
||||
uint32_t type : 2;
|
||||
};
|
||||
} ehci_link_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ehci_link_t) == 4, "size is not correct" );
|
||||
|
||||
/// Queue Element Transfer Descriptor
|
||||
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
|
||||
typedef struct {
|
||||
// Word 0 Next QTD Pointer
|
||||
ehci_link_t next;
|
||||
|
||||
// Word 1 Alternate Next QTD Pointer (not used)
|
||||
union {
|
||||
ehci_link_t alternate;
|
||||
struct {
|
||||
uint32_t : 5;
|
||||
uint32_t used : 1;
|
||||
uint32_t : 10;
|
||||
uint32_t expected_bytes : 16;
|
||||
};
|
||||
};
|
||||
|
||||
// Word 2 qTQ Token
|
||||
volatile uint32_t ping_err : 1; // For Highspeed: 0 Out, 1 Ping. Full/Slow used as error indicator
|
||||
volatile uint32_t non_hs_split_state : 1; // Used by HC to track the state of split transaction
|
||||
volatile uint32_t non_hs_missed_uframe : 1; // HC misses a complete split transaction
|
||||
volatile uint32_t xact_err : 1; // Error (Timeout, CRC, Bad PID ... )
|
||||
volatile uint32_t babble_err : 1; // Babble detected, also set Halted bit to 1
|
||||
volatile uint32_t buffer_err : 1; // Data overrun/underrun error
|
||||
volatile uint32_t halted : 1; // Serious error or STALL received
|
||||
volatile uint32_t active : 1; // Start transfer, clear by HC when complete
|
||||
|
||||
uint32_t pid : 2; // 0: OUT, 1: IN, 2 Setup
|
||||
volatile uint32_t err_count : 2; // Error Counter of consecutive errors
|
||||
volatile uint32_t current_page : 3; // Index into the qTD buffer pointer list
|
||||
uint32_t int_on_complete : 1; // Interrupt on complete
|
||||
volatile uint32_t total_bytes : 15; // Transfer bytes, decreased during transaction
|
||||
volatile uint32_t data_toggle : 1; // Data Toggle bit
|
||||
|
||||
// Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address.
|
||||
// The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
|
||||
uint32_t buffer[5];
|
||||
} ehci_qtd_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ehci_qtd_t) == 32, "size is not correct" );
|
||||
|
||||
/// Queue Head
|
||||
typedef struct TU_ATTR_ALIGNED(32) {
|
||||
// Word 0 Next QHD
|
||||
ehci_link_t next;
|
||||
|
||||
// Word 1 Endpoint Characteristics
|
||||
uint32_t dev_addr : 7; // device address
|
||||
uint32_t fl_inactive_next_xact : 1; // Only valid for Periodic with Full/Slow speed
|
||||
uint32_t ep_number : 4; // EP number
|
||||
uint32_t ep_speed : 2; // Full (0), Low (1), High (2)
|
||||
uint32_t data_toggle_control : 1; // 0 use DT in qHD, 1 use DT in qTD
|
||||
uint32_t head_list_flag : 1; // Head of the queue
|
||||
uint32_t max_packet_size : 11; // Max packet size
|
||||
uint32_t fl_ctrl_ep_flag : 1; // 1 if is Full/Low speed control endpoint
|
||||
uint32_t nak_reload : 4; // Used by HC
|
||||
|
||||
// Word 2 Endpoint Capabilities
|
||||
uint32_t int_smask : 8; // Interrupt Schedule Mask
|
||||
uint32_t fl_int_cmask : 8; // Split Completion Mask for Full/Slow speed
|
||||
uint32_t fl_hub_addr : 7; // Hub Address for Full/Slow speed
|
||||
uint32_t fl_hub_port : 7; // Hub Port for Full/Slow speed
|
||||
uint32_t mult : 2; // Transaction per micro frame
|
||||
|
||||
// Word 3 Current qTD Pointer
|
||||
volatile uint32_t qtd_addr;
|
||||
|
||||
// Word 4-11 Transfer Overlay
|
||||
volatile ehci_qtd_t qtd_overlay;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
/// Due to the fact QHD is 32 bytes aligned but occupies only 48 bytes
|
||||
/// thus there are 16 bytes padding free that we can make use of.
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t used;
|
||||
uint8_t removing;// removed from asyn list, waiting for async advance
|
||||
uint8_t pid;
|
||||
uint8_t interval_ms;// polling interval in frames (or millisecond)
|
||||
|
||||
uint8_t TU_RESERVED[4];
|
||||
|
||||
// Attached TD management, note usbh will only queue 1 TD per QHD.
|
||||
// buffer for dcache invalidate since td's buffer is modified by HC and finding initial buffer address is not trivial
|
||||
uint32_t attached_buffer;
|
||||
ehci_qtd_t *volatile attached_qtd;
|
||||
} ehci_qhd_t;
|
||||
TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
|
||||
|
||||
/// Highspeed Isochronous Transfer Descriptor (section 3.3)
|
||||
typedef struct TU_ATTR_ALIGNED(32) {
|
||||
// Word 0: Next Link Pointer
|
||||
ehci_link_t next;
|
||||
|
||||
// Word 1-8: iTD Transaction Status and Control List
|
||||
struct {
|
||||
// iTD Control
|
||||
volatile uint32_t offset : 12; // offset in bytes, from the beginning of a buffer.
|
||||
volatile uint32_t page_select : 3; // buffer page pointers the offset field in this slot should be concatenated to produce the starting memory address for this transaction. The valid range of values for this field is 0 to 6
|
||||
uint32_t int_on_complete : 1; // If this bit is set to a one, it specifies that when this transaction completes, the Host Controller should issue an interrupt at the next interrupt threshold
|
||||
volatile uint32_t length : 12; // For an OUT, this field is the number of data bytes the host controller will send during the transaction. The host controller is not required to update this field to reflect the actual number of bytes transferred during the transfer
|
||||
// For an IN, the initial value of the field is the number of bytes the host expects the endpoint to deliver. During the status update, the host controller writes back the number of bytes successfully received. The value in this register is the actual byte count
|
||||
// iTD Status
|
||||
volatile uint32_t error : 1; // Set to a one by the Host Controller during status update in the case where the host did not receive a valid response from the device (Timeout, CRC, Bad PID, etc.). This bit may only be set for isochronous IN transactions.
|
||||
volatile uint32_t babble_err : 1; // Set to a 1 by the Host Controller during status update when a babble is detected during the transaction
|
||||
volatile uint32_t buffer_err : 1; // Set to a 1 by the Host Controller during status update to indicate that the Host Controller is unable to keep up with the reception of incoming data (overrun) or is unable to supply data fast enough during transmission (underrun).
|
||||
volatile uint32_t active : 1; // Set to 1 by software to enable the execution of an isochronous transaction by the Host Controller
|
||||
} xact[8];
|
||||
|
||||
// Word 9-15 Buffer Page Pointer List (Plus)
|
||||
uint32_t BufferPointer[7];
|
||||
|
||||
// FIXME: Store meta data into buffer pointer reserved for saving memory
|
||||
//---------- HCD Area ----------
|
||||
// uint32_t used;
|
||||
// uint32_t IhdIdx;
|
||||
// uint32_t reserved[6];
|
||||
} ehci_itd_t;
|
||||
TU_VERIFY_STATIC( sizeof(ehci_itd_t) == 64, "size is not correct" );
|
||||
|
||||
/// Split (Full-Speed) Isochronous Transfer Descriptor
|
||||
typedef struct TU_ATTR_ALIGNED(32) {
|
||||
// Word 0: Next Link Pointer
|
||||
ehci_link_t next;
|
||||
|
||||
// Word 1: siTD Endpoint Characteristics
|
||||
uint32_t dev_addr : 7; ///< This field selects the specific device serving as the data source or sink.
|
||||
uint32_t : 1; ///< reserved
|
||||
uint32_t ep_number : 4; ///< This 4-bit field selects the particular endpoint number on the device serving as the data source or sink.
|
||||
uint32_t : 4; ///< This field is reserved and should be set to zero.
|
||||
uint32_t hub_addr : 7; ///< This field holds the device address of the transaction translators’ hub.
|
||||
uint32_t : 1; ///< reserved
|
||||
uint32_t port_number : 7; ///< This field is the port number of the recipient transaction translator.
|
||||
uint32_t direction : 1; ///< 0 = OUT; 1 = IN. This field encodes whether the full-speed transaction should be an IN or OUT.
|
||||
|
||||
// Word 2: Micro-frame Schedule Control
|
||||
uint8_t int_smask ; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute complete-split transactions
|
||||
uint8_t fl_int_cmask; ///< This field (along with the Activeand SplitX-statefields in the Statusbyte) are used to determine during which micro-frames the host controller should execute start-split transactions.
|
||||
uint16_t reserved ; ///< reserved
|
||||
|
||||
// Word 3: siTD Transfer Status and Control
|
||||
// Status [7:0] TODO identical to qTD Token'status --> refactor later
|
||||
volatile uint32_t : 1 ; // reserved
|
||||
volatile uint32_t split_state : 1 ;
|
||||
volatile uint32_t missed_uframe : 1 ;
|
||||
volatile uint32_t xact_err : 1 ;
|
||||
volatile uint32_t babble_err : 1 ;
|
||||
volatile uint32_t buffer_err : 1 ;
|
||||
volatile uint32_t error : 1 ;
|
||||
volatile uint32_t active : 1 ;
|
||||
// Micro-frame Schedule Control
|
||||
volatile uint32_t cmask_progress : 8 ; ///< This field is used by the host controller to record which split-completes have been executed. See Section 4.12.3.3.2 for behavioral requirements.
|
||||
volatile uint32_t total_bytes : 10 ; ///< This field is initialized by software to the total number of bytes expected in this transfer. Maximum value is 1023
|
||||
volatile uint32_t : 4 ; ///< reserved
|
||||
volatile uint32_t page_select : 1 ; ///< Used to indicate which data page pointer should be concatenated with the CurrentOffsetfield to construct a data buffer pointer
|
||||
uint32_t int_on_complete : 1 ; ///< Do not interrupt when transaction is complete. 1 = Do interrupt when transaction is complete
|
||||
uint32_t : 0 ; // padding to the end of current storage unit
|
||||
|
||||
/// Word 4-5: Buffer Pointer List
|
||||
uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
|
||||
|
||||
/*---------- Word 6 ----------*/
|
||||
ehci_link_t back;
|
||||
|
||||
/// SITD is 32-byte aligned but occupies only 28 --> 4 bytes for storing extra data
|
||||
uint8_t used;
|
||||
uint8_t ihd_idx;
|
||||
uint8_t reserved2[2];
|
||||
} ehci_sitd_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// EHCI Operational Register
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
// Bit 0-5 has maskable in interrupt enabled register
|
||||
EHCI_INT_MASK_USB = TU_BIT(0),
|
||||
EHCI_INT_MASK_ERROR = TU_BIT(1),
|
||||
EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2),
|
||||
EHCI_INT_MASK_FRAMELIST_ROLLOVER = TU_BIT(3),
|
||||
EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = TU_BIT(4),
|
||||
EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5),
|
||||
|
||||
EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
|
||||
|
||||
EHCI_INT_MASK_HC_HALTED = TU_BIT(12),
|
||||
EHCI_INT_MASK_RECLAIMATION = TU_BIT(13),
|
||||
EHCI_INT_MASK_PERIODIC_SCHED_STATUS = TU_BIT(14),
|
||||
EHCI_INT_MASK_ASYNC_SCHED_STATUS = TU_BIT(15),
|
||||
|
||||
EHCI_INT_MASK_ALL =
|
||||
EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE |
|
||||
EHCI_INT_MASK_FRAMELIST_ROLLOVER | EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR |
|
||||
EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_SOF
|
||||
};
|
||||
|
||||
enum {
|
||||
EHCI_USBCMD_FRAMELIST_SIZE_SHIFT = 2, // [2..3]
|
||||
EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT = 15,
|
||||
EHCI_USBCMD_INTERRUPT_THRESHOLD_SHIFT = 16
|
||||
};
|
||||
|
||||
enum {
|
||||
EHCI_USBCMD_RUN_STOP = TU_BIT(0), // [0..0] 1 = Run, 0 = Stop
|
||||
EHCI_USBCMD_HCRESET = TU_BIT(1), // [1..1] SW write 1 to reset HC, clear by HC when complete
|
||||
EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE = TU_BIT(4), // [4..4] Enable periodic schedule
|
||||
EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE = TU_BIT(5), // [5..5] Enable async schedule
|
||||
EHCI_USBCMD_INTR_ON_ASYNC_ADVANCE_DOORBELL = TU_BIT(6), // [6..6] Tell HC to interrupt next time it advances async list. Clear by HC
|
||||
};
|
||||
|
||||
enum {
|
||||
EHCI_PORTSC_MASK_CURRENT_CONNECT_STATUS = TU_BIT(0),
|
||||
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
|
||||
EHCI_PORTSC_MASK_PORT_EANBLED = TU_BIT(2),
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE = TU_BIT(3),
|
||||
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
|
||||
EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
|
||||
EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
|
||||
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
|
||||
EHCI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
|
||||
|
||||
EHCI_PORTSC_MASK_W1C =
|
||||
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
|
||||
EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE |
|
||||
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
|
||||
};
|
||||
|
||||
typedef volatile struct {
|
||||
union {
|
||||
uint32_t command; // 0x00
|
||||
|
||||
struct {
|
||||
uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
|
||||
uint32_t reset : 1 ; ///< SW write 1 to reset HC, clear by HC when complete
|
||||
uint32_t framelist_size : 2 ; ///< Frame List size 0: 1024, 1: 512, 2: 256
|
||||
uint32_t periodic_enable : 1 ; ///< This bit controls whether the host controller skips processing the Periodic Schedule. Values mean: 0b Do not process the Periodic Schedule 1b Use the PERIODICLISTBASE register to access the Periodic Schedule.
|
||||
uint32_t async_enable : 1 ; ///< This bit controls whether the host controller skips processing the Asynchronous Schedule. Values mean: 0b Do not process the Asynchronous Schedule 1b Use the ASYNCLISTADDR register to access the Asynchronous Schedule.
|
||||
uint32_t async_adv_doorbell : 1 ; ///< Tell HC to interrupt next time it advances async list. Clear by HC
|
||||
uint32_t light_reset : 1 ; ///< Reset HC without affecting ports state
|
||||
uint32_t async_park_count : 2 ; ///< not used by tinyusb
|
||||
uint32_t : 1 ;
|
||||
uint32_t async_park_enable : 1 ; ///< Enable park mode, not used by tinyusb
|
||||
uint32_t : 3 ;
|
||||
uint32_t nxp_framelist_size_msb : 1 ; ///< NXP customized : Bit 2 of the Frame List Size bits \n 011b: 128 elements \n 100b: 64 elements \n 101b: 32 elements \n 110b: 16 elements \n 111b: 8 elements
|
||||
uint32_t int_threshold : 8 ; ///< Default 08h. Interrupt rate in unit of micro frame
|
||||
}command_bm;
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t status; // 0x04
|
||||
|
||||
struct {
|
||||
uint32_t usb : 1 ; ///< qTD with IOC is retired
|
||||
uint32_t usb_error : 1 ; ///< qTD retired due to error
|
||||
uint32_t port_change_detect : 1 ; ///< Set when PortOwner or ForcePortResume change from 0 -> 1
|
||||
uint32_t framelist_rollover : 1 ; ///< R/WC The Host Controller sets this bit to a one when the Frame List Index(see Section 2.3.4) rolls over from its maximum value to zero. The exact value at which the rollover occurs depends on the frame list size. For example, if the frame list size (as programmed in the Frame List Sizefield of the USBCMD register) is 1024, the Frame Index Registerrolls over every time FRINDEX[13] toggles. Similarly, if the size is 512, the Host Controller sets this bit to a one every time FRINDEX[12] toggles.
|
||||
uint32_t pci_host_system_error : 1 ; ///< R/WC (not used by NXP) The Host Controller sets this bit to 1 when a serious error occurs during a host system access involving the Host Controller module. In a PCI system, conditions that set this bit to 1 include PCI Parity error, PCI Master Abort, and PCI Target Abort. When this error occurs, the Host Controller clears the Run/Stop bit in the Command register to prevent further execution of the scheduled TDs.
|
||||
uint32_t async_adv : 1 ; ///< Async Advance interrupt
|
||||
uint32_t : 1 ;
|
||||
uint32_t nxp_int_sof : 1 ; ///< NXP customized: this bit will be set every 125us and can be used by host controller driver as a time base.
|
||||
uint32_t : 4 ;
|
||||
uint32_t hc_halted : 1 ; ///< Opposite value to run_stop bit.
|
||||
uint32_t reclamation : 1 ; ///< Used to detect empty async shecudle
|
||||
uint32_t periodic_status : 1 ; ///< Periodic schedule status
|
||||
uint32_t async_status : 1 ; ///< Async schedule status
|
||||
uint32_t : 2 ;
|
||||
uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected and the packet is on the asynchronous schedule.
|
||||
uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the periodic schedule.
|
||||
uint32_t : 12 ;
|
||||
}status_bm;
|
||||
};
|
||||
|
||||
union{
|
||||
uint32_t inten; // 0x08
|
||||
|
||||
struct {
|
||||
uint32_t usb : 1 ;
|
||||
uint32_t usb_error : 1 ;
|
||||
uint32_t port_change_detect : 1 ;
|
||||
uint32_t framelist_rollover : 1 ;
|
||||
uint32_t pci_host_system_error : 1 ;
|
||||
uint32_t async_adv : 1 ;
|
||||
uint32_t : 1 ;
|
||||
uint32_t nxp_int_sof : 1 ;
|
||||
uint32_t : 10 ;
|
||||
uint32_t nxp_int_async : 1 ;
|
||||
uint32_t nxp_int_period : 1 ;
|
||||
uint32_t : 12 ;
|
||||
}inten_bm;
|
||||
};
|
||||
|
||||
uint32_t frame_index ; ///< 0x0C Micro frame counter
|
||||
uint32_t ctrl_ds_seg ; ///< 0x10 Control Data Structure Segment
|
||||
uint32_t periodic_list_base ; ///< 0x14 Beginning address of perodic frame list
|
||||
uint32_t async_list_addr ; ///< 0x18 Address of next async QHD to be executed
|
||||
uint32_t nxp_tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
|
||||
uint32_t reserved[8] ;
|
||||
uint32_t config_flag ; ///< 0x40 not used by NXP
|
||||
|
||||
union {
|
||||
// mixed with RW and R/WC bits, care should be taken when writing to this register
|
||||
uint32_t portsc ; ///< 0x44 port status and control
|
||||
const struct {
|
||||
uint32_t current_connect_status : 1; ///< 00: 0: No device, 1: Device is present on port
|
||||
uint32_t connect_status_change : 1; ///< 01: [R/WC] Change in Current Connect Status
|
||||
uint32_t port_enabled : 1; ///< 02: Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
|
||||
uint32_t port_enable_change : 1; ///< 03: [R/WC] Port Enabled has changed
|
||||
uint32_t over_current_active : 1; ///< 04: Port has an over-current condition
|
||||
uint32_t over_current_change : 1; ///< 05: [R/WC] Change to Over-current Active
|
||||
uint32_t force_port_resume : 1; ///< 06: Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
|
||||
uint32_t suspend : 1; ///< 07: Port in suspend state
|
||||
uint32_t port_reset : 1; ///< 08: 1=Port is in Reset. 0=Port is not in Reset
|
||||
uint32_t nxp_highspeed_status : 1; ///< 09: NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
|
||||
uint32_t line_status : 2; ///< 10-11: D+/D- state: 00: SE0, 10: J-state, 01: K-state
|
||||
uint32_t port_power : 1; ///< 12: 0= power off, 1= power on
|
||||
uint32_t port_owner : 1; ///< 13: not used by NXP
|
||||
uint32_t port_indicator_control : 2; ///< 14-15: 00b: off, 01b: Amber, 10b: green, 11b: undefined
|
||||
uint32_t port_test_control : 4; ///< 16-19: Port test mode, not used by tinyusb
|
||||
uint32_t wake_on_connect_enable : 1; ///< 20: Enables device connects as wake-up events
|
||||
uint32_t wake_on_disconnect_enable : 1; ///< 21: Enables device disconnects as wake-up events
|
||||
uint32_t wake_on_over_current_enable : 1; ///< 22: Enables over-current conditions as wake-up events
|
||||
uint32_t nxp_phy_clock_disable : 1; ///< 23: NXP customized: the PHY can be put into Low Power Suspend – Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
|
||||
uint32_t nxp_port_force_fullspeed : 1; ///< 24: NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
|
||||
uint32_t TU_RESERVED : 1; ///< 25
|
||||
uint32_t nxp_port_speed : 2; ///< 26-27: NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
|
||||
uint32_t TU_RESERVED : 4;
|
||||
}portsc_bm;
|
||||
};
|
||||
} ehci_registers_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Capability Registers
|
||||
//--------------------------------------------------------------------+
|
||||
typedef volatile struct {
|
||||
uint8_t caplength; // 0x00
|
||||
uint8_t TU_RESERVED; // 0x01
|
||||
uint16_t hciversion; // 0x02
|
||||
|
||||
union {
|
||||
uint32_t hcsparams; // 0x04
|
||||
struct {
|
||||
uint32_t num_ports : 4; // [00:03]
|
||||
uint32_t port_power_control : 1; // [04]
|
||||
uint32_t TU_RESERVED : 2; // [05:06]
|
||||
uint32_t port_route_rule : 1; // [07]
|
||||
uint32_t n_pcc : 4; // [08:11] Number of Ports per Companion Controller
|
||||
uint32_t n_cc : 4; // [12:15] Number of Companion Controllers
|
||||
uint32_t port_ind : 1; // [16] Port Indicators
|
||||
uint32_t TU_RESERVED : 3; // [17:19]
|
||||
uint32_t n_ptt : 4; // [20:23] ChipIdea: Number of Ports per Transaction Translator
|
||||
uint32_t n_tt : 4; // [24:27] ChipIdea: Number of Transaction Translators
|
||||
uint32_t TU_RESERVED : 4; // [28:31]
|
||||
} hcsparams_bm;
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t hccparams; // 0x08
|
||||
struct {
|
||||
uint32_t addr_64bit : 1; // [00] 64-bit Addressing Capability
|
||||
uint32_t programmable_frame_list_flag : 1; // [01] Programmable Frame List Flag
|
||||
uint32_t async_park_cap : 1; // [02] Asynchronous Schedule Park Capability
|
||||
uint32_t TU_RESERVED : 1; // [03]
|
||||
uint32_t iso_schedule_threshold : 4; // [4:7] Isochronous Scheduling Threshold
|
||||
uint32_t eecp : 8; // [8:15] EHCI Extended Capabilities Pointer
|
||||
uint32_t TU_RESERVED : 16;// [16:31]
|
||||
} hccparams_bm;
|
||||
};
|
||||
|
||||
uint32_t hcsp_portroute; // 0x0C HCSP Port Route Register
|
||||
} ehci_cap_registers_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(ehci_cap_registers_t) == 16, "size is not correct");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_EHCI_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user