summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-11-18 10:18:27 +0700
committerhathach <[email protected]>2025-11-18 10:18:27 +0700
commit6c140930591fd49725a1ec1434dfb841560bc6ae (patch)
tree8433576ea573a2f6f5d88ff309716f3ad5538f05 /src
parent619ef71fdb5da5dbfabe76189ead4ab97837ec12 (diff)
parent66c84528f67c0eedc23d25221500d820e702d93f (diff)
Merge branch 'master' into fork/HiFiPhile/xfer_close
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt17
-rw-r--r--src/class/audio/audio.h1828
-rw-r--r--src/class/audio/audio_device.c1796
-rw-r--r--src/class/audio/audio_device.h481
-rwxr-xr-xsrc/class/bth/bth_device.c130
-rwxr-xr-xsrc/class/bth/bth_device.h14
-rw-r--r--src/class/cdc/cdc.h112
-rw-r--r--src/class/cdc/cdc_device.c446
-rw-r--r--src/class/cdc/cdc_device.h53
-rw-r--r--src/class/cdc/cdc_host.c2564
-rw-r--r--src/class/cdc/cdc_host.h109
-rw-r--r--src/class/cdc/cdc_rndis.h6
-rw-r--r--src/class/cdc/cdc_rndis_host.c2
-rw-r--r--src/class/cdc/cdc_rndis_host.h6
-rw-r--r--src/class/cdc/serial/ch34x.h74
-rw-r--r--src/class/cdc/serial/cp210x.h56
-rw-r--r--src/class/cdc/serial/ftdi_sio.h378
-rw-r--r--src/class/cdc/serial/pl2303.h159
-rw-r--r--src/class/dfu/dfu.h6
-rw-r--r--src/class/dfu/dfu_device.c27
-rw-r--r--src/class/dfu/dfu_device.h12
-rw-r--r--src/class/dfu/dfu_rt_device.h6
-rw-r--r--src/class/hid/hid.h597
-rw-r--r--src/class/hid/hid_device.h10
-rw-r--r--src/class/hid/hid_host.c94
-rw-r--r--src/class/hid/hid_host.h18
-rw-r--r--src/class/midi/midi_device.c501
-rw-r--r--src/class/midi/midi_device.h134
-rw-r--r--src/class/midi/midi_host.c85
-rw-r--r--src/class/msc/msc.h33
-rw-r--r--src/class/msc/msc_device.c466
-rw-r--r--src/class/msc/msc_device.h82
-rw-r--r--src/class/msc/msc_host.c46
-rw-r--r--src/class/msc/msc_host.h4
-rw-r--r--src/class/mtp/mtp.h888
-rw-r--r--src/class/mtp/mtp_device.c540
-rw-r--r--src/class/mtp/mtp_device.h169
-rw-r--r--src/class/net/ecm_rndis_device.c53
-rw-r--r--src/class/net/ncm.h4
-rw-r--r--src/class/net/ncm_device.c40
-rw-r--r--src/class/net/net_device.h18
-rw-r--r--src/class/usbtmc/usbtmc.h4
-rw-r--r--src/class/usbtmc/usbtmc_device.c777
-rw-r--r--src/class/usbtmc/usbtmc_device.h7
-rw-r--r--src/class/vendor/vendor_device.c74
-rw-r--r--src/class/vendor/vendor_device.h10
-rw-r--r--src/class/vendor/vendor_host.h6
-rw-r--r--src/class/video/video.h10
-rw-r--r--src/class/video/video_device.c235
-rw-r--r--src/class/video/video_device.h26
-rw-r--r--src/common/tusb_common.h184
-rw-r--r--src/common/tusb_compiler.h68
-rw-r--r--src/common/tusb_debug.h25
-rw-r--r--src/common/tusb_fifo.c458
-rw-r--r--src/common/tusb_fifo.h69
-rw-r--r--src/common/tusb_mcu.h390
-rw-r--r--src/common/tusb_private.h45
-rw-r--r--src/common/tusb_types.h76
-rw-r--r--src/common/tusb_verify.h16
-rw-r--r--src/device/dcd.h17
-rw-r--r--src/device/usbd.c279
-rw-r--r--src/device/usbd.h481
-rw-r--r--src/device/usbd_control.c6
-rw-r--r--src/device/usbd_pvt.h14
-rw-r--r--src/host/hcd.h6
-rw-r--r--src/host/usbh.c309
-rw-r--r--src/host/usbh.h90
-rw-r--r--src/host/usbh_pvt.h9
-rw-r--r--src/osal/osal.h25
-rw-r--r--src/osal/osal_freertos.h117
-rw-r--r--src/osal/osal_mynewt.h26
-rw-r--r--src/osal/osal_none.h32
-rw-r--r--src/osal/osal_pico.h26
-rw-r--r--src/osal/osal_rtthread.h26
-rw-r--r--src/osal/osal_rtx4.h19
-rw-r--r--src/osal/osal_zephyr.h29
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c69
-rw-r--r--src/portable/bridgetek/ft9xx/dcd_ft9xx.c15
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c67
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c200
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c16
-rw-r--r--src/portable/ehci/ehci.c85
-rw-r--r--src/portable/ehci/ehci.h6
-rw-r--r--src/portable/ehci/ehci_api.h4
-rw-r--r--src/portable/espressif/esp32sx/dcd_esp32sx.c889
-rw-r--r--src/portable/mentor/musb/musb_max32.h10
-rw-r--r--src/portable/microchip/pic/dcd_pic.c16
-rw-r--r--src/portable/microchip/pic32mz/dcd_pic32mz.c21
-rw-r--r--src/portable/microchip/samd/dcd_samd.c35
-rw-r--r--src/portable/microchip/samd/hcd_samd.c761
-rw-r--r--src/portable/microchip/samg/dcd_samg.c15
-rw-r--r--src/portable/microchip/samx7x/dcd_samx7x.c17
-rw-r--r--src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c297
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c16
-rw-r--r--src/portable/nuvoton/nuc120/dcd_nuc120.c13
-rw-r--r--src/portable/nuvoton/nuc121/dcd_nuc121.c26
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c23
-rw-r--r--src/portable/nxp/khci/dcd_khci.c64
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.c15
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.h4
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c16
-rw-r--r--src/portable/ohci/ohci.c484
-rw-r--r--src/portable/ohci/ohci.h190
-rw-r--r--src/portable/ohci/ohci_nxp.h (renamed from src/portable/nxp/lpc17_40/hcd_lpc17_40.c)48
-rw-r--r--src/portable/raspberrypi/pio_usb/dcd_pio_usb.c14
-rw-r--r--src/portable/raspberrypi/pio_usb/hcd_pio_usb.c11
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c3
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c16
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.c4
-rw-r--r--src/portable/renesas/rusb2/rusb2_type.h6
-rw-r--r--src/portable/sony/cxd56/dcd_cxd56.c13
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c50
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_at32.h225
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_ch32.h5
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h75
-rw-r--r--src/portable/sunxi/dcd_sunxi_musb.c32
-rw-r--r--src/portable/sunxi/musb_def.h4
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c211
-rw-r--r--src/portable/synopsys/dwc2/dwc2_at32.h122
-rw-r--r--src/portable/synopsys/dwc2/dwc2_bcm.h4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c47
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h12
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h21
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py6
-rw-r--r--src/portable/synopsys/dwc2/dwc2_nrf.h61
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h109
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h8
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c5
-rw-r--r--src/portable/ti/msp430x5xx/dcd_msp430x5xx.c34
-rw-r--r--src/portable/valentyusb/eptri/dcd_eptri.c15
-rw-r--r--src/portable/valentyusb/eptri/dcd_eptri.h6
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c13
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c27
-rw-r--r--src/portable/wch/hcd_ch32_usbfs.c679
-rw-r--r--src/tinyusb.mk1
-rw-r--r--src/tusb.c89
-rw-r--r--src/tusb.h27
-rw-r--r--src/tusb_option.h106
-rw-r--r--src/typec/pd_types.h4
-rw-r--r--src/typec/tcd.h4
-rw-r--r--src/typec/usbc.c25
-rw-r--r--src/typec/usbc.h10
143 files changed, 13438 insertions, 8259 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 55c52033c..48dc75e50 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,11 +1,8 @@
-# TODO more docs and example on how to use this file
-# TINYUSB_TARGET_PREFIX and TINYUSB_TARGET_SUFFIX can be used to change the name of the target
-
cmake_minimum_required(VERSION 3.20)
-# Add tinyusb to a existing target
-function(tinyusb_target_add TARGET)
- target_sources(${TARGET} PRIVATE
+# Get TinyUSB sources. Note: DCD and HCD drivers are not included
+function(tinyusb_sources_get OUTPUT_VAR)
+ set(${OUTPUT_VAR}
# common
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tusb.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c
@@ -19,6 +16,7 @@ function(tinyusb_target_add TARGET)
${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
@@ -34,7 +32,14 @@ function(tinyusb_target_add TARGET)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c
# typec
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/typec/usbc.c
+ PARENT_SCOPE
)
+endfunction()
+
+# Add tinyusb to a existing target
+function(tinyusb_target_add TARGET)
+ tinyusb_sources_get(TINYUSB_SRC)
+ target_sources(${TARGET} PRIVATE ${TINYUSB_SRC})
target_include_directories(${TARGET} PUBLIC
${CMAKE_CURRENT_FUNCTION_LIST_DIR}
# TODO for net driver, should be removed/changed
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 0d1acadcc..cff38cc22 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -30,8 +30,8 @@
* Currently only MIDI subclass is supported
* @{ */
-#ifndef _TUSB_AUDIO_H__
-#define _TUSB_AUDIO_H__
+#ifndef TUSB_AUDIO_H__
+#define TUSB_AUDIO_H__
#include "common/tusb_common.h"
@@ -39,917 +39,1255 @@
extern "C" {
#endif
-/// Audio Device Class Codes
+//--------------------------------------------------------------------+
+// GENERIC AUDIO CLASS CODES (COMMON TO UAC1 AND UAC2)
+//--------------------------------------------------------------------+
/// A.2 - Audio Function Subclass Codes
-typedef enum
-{
+typedef enum {
AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
} audio_function_subclass_type_t;
/// A.3 - Audio Function Protocol Codes
-typedef enum
-{
- AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
- AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+typedef enum {
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V1 = 0x00,///< Version 1.0 - same as undefined for backward compatibility
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20,///< Version 2.0
} audio_function_protocol_code_t;
/// A.5 - Audio Interface Subclass Codes
-typedef enum
-{
+typedef enum {
AUDIO_SUBCLASS_UNDEFINED = 0x00,
- AUDIO_SUBCLASS_CONTROL , ///< Audio Control
- AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
- AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
+ AUDIO_SUBCLASS_CONTROL, ///< Audio Control
+ AUDIO_SUBCLASS_STREAMING, ///< Audio Streaming
+ AUDIO_SUBCLASS_MIDI_STREAMING,///< MIDI Streaming
} audio_subclass_type_t;
/// A.6 - Audio Interface Protocol Codes
-typedef enum
-{
- AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
- AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+typedef enum {
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V1 = 0x00,///< Version 1.0 - same as undefined for backward compatibility
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20,///< Version 2.0
} audio_interface_protocol_code_t;
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types
+typedef enum {
+ AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
+ AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
+ AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
+} audio_terminal_type_t;
+
+/// 2.2 - Audio Class-Input Terminal Types
+typedef enum {
+ AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
+ AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
+ AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
+ AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
+ AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
+ AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
+ AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
+} audio_terminal_input_type_t;
+
+/// 2.3 - Audio Class-Output Terminal Types
+typedef enum {
+ AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
+ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
+ AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
+ AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
+ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
+ AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
+ AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
+ AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
+} audio_terminal_output_type_t;
+
+/// Rest is yet to be implemented
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 1.0 (UAC1) DEFINITIONS
+//--------------------------------------------------------------------+
+
+/// A.5 - Audio Class-Specific AC Interface Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO10_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO10_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO10_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO10_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO10_CS_AC_INTERFACE_PROCESSING_UNIT = 0x07,
+ AUDIO10_CS_AC_INTERFACE_EXTENSION_UNIT = 0x08,
+} audio10_cs_ac_interface_subtype_t;
+
+/// A.6 - Audio Class-Specific AS Interface Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO10_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+} audio10_cs_as_interface_subtype_t;
+
+/// A.8 - Audio Class-Specific EP Descriptor Subtypes UAC1
+typedef enum {
+ AUDIO10_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO10_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio10_cs_ep_subtype_t;
+
+/// A.9 - Audio Class-Specific Request Codes UAC1
+typedef enum {
+ AUDIO10_CS_REQ_UNDEF = 0x00,
+ AUDIO10_CS_REQ_SET_CUR = 0x01,
+ AUDIO10_CS_REQ_GET_CUR = 0x81,
+ AUDIO10_CS_REQ_SET_MIN = 0x02,
+ AUDIO10_CS_REQ_GET_MIN = 0x82,
+ AUDIO10_CS_REQ_SET_MAX = 0x03,
+ AUDIO10_CS_REQ_GET_MAX = 0x83,
+ AUDIO10_CS_REQ_SET_RES = 0x04,
+ AUDIO10_CS_REQ_GET_RES = 0x84,
+ AUDIO10_CS_REQ_SET_MEM = 0x05,
+ AUDIO10_CS_REQ_GET_MEM = 0x85,
+ AUDIO10_CS_REQ_GET_STAT = 0xFF,
+} audio10_cs_req_t;
+
+/// A.10.1 - Terminal Control Selectors UAC1
+typedef enum {
+ AUDIO10_TE_CTRL_UNDEF = 0x00,
+ AUDIO10_TE_CTRL_COPY_PROTECT = 0x01,
+} audio10_terminal_control_selector_t;
+
+/// A.10.2 - Feature Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_FU_CTRL_UNDEF = 0x00,
+ AUDIO10_FU_CTRL_MUTE = 0x01,
+ AUDIO10_FU_CTRL_VOLUME = 0x02,
+ AUDIO10_FU_CTRL_BASS = 0x03,
+ AUDIO10_FU_CTRL_MID = 0x04,
+ AUDIO10_FU_CTRL_TREBLE = 0x05,
+ AUDIO10_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO10_FU_CTRL_AGC = 0x07,
+ AUDIO10_FU_CTRL_DELAY = 0x08,
+ AUDIO10_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO10_FU_CTRL_LOUDNESS = 0x0A,
+} audio10_feature_unit_control_selector_t;
+
+/// A.10.3.1 - Up/Down-mix Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_UD_CTRL_UNDEF = 0x00,
+ AUDIO10_UD_CTRL_ENABLE = 0x01,
+ AUDIO10_UD_CTRL_MODE_SELECT = 0x02,
+} audio10_up_down_mix_control_selector_t;
+
+/// A.10.3.2 - Dolby Prologic Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_DP_CTRL_UNDEF = 0x00,
+ AUDIO10_DP_CTRL_ENABLE = 0x01,
+ AUDIO10_DP_CTRL_MODE_SELECT = 0x02,
+} audio10_dolby_prologic_control_selector_t;
+
+/// A.10.3.3 - 3D Stereo Extender Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_3D_CTRL_UNDEF = 0x00,
+ AUDIO10_3D_CTRL_ENABLE = 0x01,
+ AUDIO10_3D_CTRL_SPACIOUSNESS = 0x02,
+} audio10_3d_stereo_extender_control_selector_t;
+
+/// A.10.3.4 - Reverberation Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_RV_CTRL_UNDEF = 0x00,
+ AUDIO10_RV_CTRL_ENABLE = 0x01,
+ AUDIO10_RV_CTRL_REVERB_LEVEL = 0x02,
+ AUDIO10_RV_CTRL_REVERB_TIME = 0x03,
+ AUDIO10_RV_CTRL_REVERB_FEEDBACK = 0x04,
+} audio10_reverberation_control_selector_t;
+
+/// A.10.3.5 - Chorus Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_CH_CTRL_UNDEF = 0x00,
+ AUDIO10_CH_CTRL_ENABLE = 0x01,
+ AUDIO10_CH_CTRL_CHORUS_LEVEL = 0x02,
+ AUDIO10_CH_CTRL_CHORUS_RATE = 0x03,
+ AUDIO10_CH_CTRL_CHORUS_DEPTH = 0x04,
+} audio10_chorus_control_selector_t;
+
+/// A.10.3.6 - Dynamic Range Compressor Processing Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_DR_CTRL_UNDEF = 0x00,
+ AUDIO10_DR_CTRL_ENABLE = 0x01,
+ AUDIO10_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO10_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO10_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO10_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO10_DR_CTRL_RELEASE_TIME = 0x06,
+} audio10_dynamic_range_compression_control_selector_t;
+
+/// A.10.4 - Extension Unit Control Selectors UAC1
+typedef enum {
+ AUDIO10_XU_CTRL_UNDEF = 0x00,
+ AUDIO10_XU_CTRL_ENABLE = 0x01,
+} audio10_extension_unit_control_selector_t;
+
+/// A.10.5 - Endpoint Control Selectors UAC1
+typedef enum {
+ AUDIO10_EP_CTRL_UNDEF = 0x00,
+ AUDIO10_EP_CTRL_SAMPLING_FREQ = 0x01,
+ AUDIO10_EP_CTRL_PITCH = 0x02,
+} audio10_ep_control_selector_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC1
+typedef enum {
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ = 0x01,
+ AUDIO10_CS_AS_ISO_DATA_EP_ATT_PITCH = 0x02,
+} audio10_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC1
+typedef enum {
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio10_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Feature Unit Controls UAC1
+typedef enum {
+ AUDIO10_FU_CONTROL_BM_MUTE = 1 << 0,
+ AUDIO10_FU_CONTROL_BM_VOLUME = 1 << 1,
+ AUDIO10_FU_CONTROL_BM_BASS = 1 << 2,
+ AUDIO10_FU_CONTROL_BM_MID = 1 << 3,
+ AUDIO10_FU_CONTROL_BM_TREBLE = 1 << 4,
+ AUDIO10_FU_CONTROL_BM_GRAPHIC_EQUALIZER = 1 << 5,
+ AUDIO10_FU_CONTROL_BM_AGC = 1 << 6,
+ AUDIO10_FU_CONTROL_BM_DELAY = 1 << 7,
+ AUDIO10_FU_CONTROL_BM_BASS_BOOST = 1 << 8,
+ AUDIO10_FU_CONTROL_BM_LOUDNESS = 1 << 9,
+} audio10_feature_unit_control_bitmap_t;
+
+/// A.1 - Audio Class-Format Type Codes UAC1
+typedef enum {
+ AUDIO10_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO10_FORMAT_TYPE_I = 0x01,
+ AUDIO10_FORMAT_TYPE_II = 0x02,
+ AUDIO10_FORMAT_TYPE_III = 0x03,
+} audio10_format_type_t;
+
+// A.1.1 - Audio Class-Audio Data Format Type I UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_I_PCM = 0x0001,
+ AUDIO10_DATA_FORMAT_TYPE_I_PCM8 = 0x0002,
+ AUDIO10_DATA_FORMAT_TYPE_I_IEEE_FLOAT = 0x0003,
+ AUDIO10_DATA_FORMAT_TYPE_I_ALAW = 0x0004,
+ AUDIO10_DATA_FORMAT_TYPE_I_MULAW = 0x0005,
+} audio10_data_format_type_I_t;
+
+// A.1.2 - Audio Class-Audio Data Format Type II UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_II_MPEG = 0x1001,
+ AUDIO10_DATA_FORMAT_TYPE_II_AC3 = 0x1002,
+} audio10_data_format_type_II_t;
+
+// A.1.3 - Audio Class-Audio Data Format Type III UAC1
+typedef enum {
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_AC3_1 = 0x2001,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG1_L1_1 = 0x2002,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG1_L23_1 = 0x2003,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_EXT_1 = 0x2004,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_L1_LS_1 = 0x2005,
+ AUDIO10_DATA_FORMAT_TYPE_III_IEC1937_MPEG2_L23_LS_1 = 0x2006,
+} audio10_data_format_type_III_t;
+
+/// Audio Class-Audio Channel Configuration UAC1 (Table A-7)
+typedef enum {
+ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED = 0x0000,
+ AUDIO10_CHANNEL_CONFIG_LEFT_FRONT = 0x0001,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_FRONT = 0x0002,
+ AUDIO10_CHANNEL_CONFIG_CENTER_FRONT = 0x0004,
+ AUDIO10_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x0008,
+ AUDIO10_CHANNEL_CONFIG_LEFT_SURROUND = 0x0010,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_SURROUND = 0x0020,
+ AUDIO10_CHANNEL_CONFIG_LEFT_OF_CENTER = 0x0040,
+ AUDIO10_CHANNEL_CONFIG_RIGHT_OF_CENTER = 0x0080,
+ AUDIO10_CHANNEL_CONFIG_SURROUND = 0x0100,
+ AUDIO10_CHANNEL_CONFIG_SIDE_LEFT = 0x0200,
+ AUDIO10_CHANNEL_CONFIG_SIDE_RIGHT = 0x0400,
+ AUDIO10_CHANNEL_CONFIG_TOP = 0x0800,
+} audio10_channel_config_t;
+
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 1.0 (UAC1) DESCRIPTORS
+//--------------------------------------------------------------------+
+
+/// AUDIO Class-Specific AC Interface Header Descriptor UAC1 (4.3.2)
+#define audio10_desc_cs_ac_interface_n_t(numInterfaces) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_HEADER. */ \
+ uint16_t bcdADC; /* Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: 0x0100 for UAC1. */ \
+ uint16_t wTotalLength; /* Total number of bytes returned for the class-specific AudioControl interface descriptor. */ \
+ uint8_t bInCollection; /* The number of AudioStreaming and MIDIStreaming interfaces in the Audio Interface Collection. */ \
+ uint8_t baInterfaceNr[numInterfaces]; /* Interface number of the AudioStreaming or MIDIStreaming interface in the Collection. */ \
+ }
+
+/// AUDIO Input Terminal Descriptor UAC1 (4.3.2.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 12.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal.
+ uint8_t bAssocTerminal; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bNrChannels; ///< Number of logical output channels in the Terminal's output audio channel cluster.
+ uint16_t wChannelConfig; ///< Describes the spatial location of the logical channels.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first logical channel.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Input Terminal.
+} audio10_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor UAC1 (4.3.2.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal.
+ uint8_t bAssocTerminal; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Output Terminal.
+} audio10_desc_output_terminal_t;
+
+/// AUDIO Mixer Unit Descriptor UAC1 (4.3.2.3)
+#define audio10_desc_mixer_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 10+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_MIXER_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Mixer Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Mixer Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bmControls[numControlBytes]; /* Mixer Unit Controls bitmap. */ \
+ uint8_t iMixer; /* Index of a string descriptor, describing the Mixer Unit. */ \
+ }
+
+/// AUDIO Selector Unit Descriptor UAC1 (4.3.2.4)
+#define audio10_desc_selector_unit_n_t(numInputPins) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 6+p. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_SELECTOR_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Selector Unit are connected. */ \
+ uint8_t iSelector; /* Index of a string descriptor, describing the Selector Unit. */ \
+ }
+
+/// AUDIO Feature Unit Descriptor UAC1 (4.3.2.5)
+#define audio10_desc_feature_unit_n_t(numChannels, controlSize) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 7+(ch+1)*n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint8_t bSourceID; /* ID of the Unit or Terminal to which this Feature Unit is connected. */ \
+ uint8_t bControlSize; /* Size in bytes of an element of the bmaControls() array. */ \
+ uint8_t bmaControls[(numChannels + 1) * controlSize]; /* Control bitmaps for master + logical channels. */ \
+ uint8_t iFeature; /* Index of a string descriptor, describing this Feature Unit. */ \
+ }
+
+/// AUDIO Processing Unit Descriptor UAC1 (4.3.2.6)
+#define audio10_desc_processing_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 13+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_PROCESSING_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint16_t wProcessType; /* Constant identifying the type of processing this Unit is performing. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Processing Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Processing Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bControlSize; /* Size in bytes of the bmControls field. */ \
+ uint8_t bmControls[numControlBytes]; /* Processing Unit Controls bitmap. */ \
+ uint8_t iProcessing; /* Index of a string descriptor, describing the Processing Unit. */ \
+ }
+
+/// AUDIO Extension Unit Descriptor UAC1 (4.3.2.7)
+#define audio10_desc_extension_unit_n_t(numInputPins, numControlBytes) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 13+p+n. */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AC_INTERFACE_EXTENSION_UNIT. */ \
+ uint8_t bUnitID; /* Constant uniquely identifying the Unit within the audio function. */ \
+ uint16_t wExtensionCode; /* Vendor-specific code identifying the Extension Unit. */ \
+ uint8_t bNrInPins; /* Number of Input Pins of this Unit: p. */ \
+ uint8_t baSourceID[numInputPins]; /* ID of the Unit or Terminal to which Input Pins of this Extension Unit are connected. */ \
+ uint8_t bNrChannels; /* Number of logical output channels in the Extension Unit's output audio channel cluster. */ \
+ uint16_t wChannelConfig; /* Describes the spatial location of the logical channels. */ \
+ uint8_t iChannelNames; /* Index of a string descriptor, describing the name of the first logical channel. */ \
+ uint8_t bControlSize; /* Size in bytes of the bmControls field. */ \
+ uint8_t bmControls[numControlBytes]; /* Extension Unit Controls bitmap. */ \
+ uint8_t iExtension; /* Index of a string descriptor, describing the Extension Unit. */ \
+ }
+
+/// AUDIO Class-Specific AS Interface Descriptor UAC1 (4.5.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 7.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink; ///< The Terminal ID of the Terminal to which the endpoint of this interface is connected.
+ uint8_t bDelay; ///< Expressed in number of frames.
+ uint16_t wFormatTag; ///< The Audio Data Format that has to be used to communicate with this interface.
+} audio10_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor UAC1 (2.2.5)
+#define audio10_desc_type_I_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint8_t bNrChannels; /* Indicates the number of physical channels in the audio data stream. */ \
+ uint8_t bSubFrameSize; /* The number of bytes occupied by one audio subframe. */ \
+ uint8_t bBitResolution; /* The number of effectively used bits from the available bits in an audio subframe. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// AUDIO Type II Format Type Descriptor UAC1 (2.3.5)
+#define audio10_desc_type_II_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 9+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint16_t wMaxBitRate; /* Indicates the maximum number of bits per second this interface can handle. */ \
+ uint16_t wSamplesPerFrame; /* Indicates the number of PCM audio samples contained in one encoded audio frame. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// AUDIO Type III Format Type Descriptor UAC1 (2.4.5)
+#define audio10_desc_type_III_format_n_t(numSamFreq) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* Size of this descriptor in bytes: 8+(ns*3). */ \
+ uint8_t bDescriptorType; /* Descriptor Type. Value: TUSB_DESC_CS_INTERFACE. */ \
+ uint8_t bDescriptorSubType; /* Descriptor SubType. Value: AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE. */ \
+ uint8_t bFormatType; /* Constant identifying the Format Type the AudioStreaming interface is using. */ \
+ uint8_t bNrChannels; /* Indicates the number of physical channels in the audio data stream. */ \
+ uint8_t bSubFrameSize; /* The number of bytes occupied by one audio subframe. */ \
+ uint8_t bBitResolution; /* The number of effectively used bits from the available bits in an audio subframe. */ \
+ uint8_t bSamFreqType; /* Indicates how the sampling frequency can be programmed. */ \
+ uint8_t tSamFreq[numSamFreq * 3]; /* Sampling frequency or lower/upper bounds in Hz for the sampling frequency range. */ \
+ }
+
+/// Standard AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_ENDPOINT.
+ uint8_t bEndpointAddress;///< The address of the endpoint on the USB device described by this descriptor.
+ struct TU_ATTR_PACKED {
+ 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; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.
+ uint8_t bInterval; ///< Interval for polling endpoint for data transfers.
+ uint8_t bRefresh; ///< The rate at which the endpoint is refreshed.
+ uint8_t bSynchAddress; ///< The address of the endpoint used to send synchronization information for the data endpoint.
+} audio10_desc_as_iso_data_ep_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 7.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO10_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes; ///< Bit 0: Sampling Frequency, Bit 1: Pitch, Bit 7: MaxPacketsOnly.
+ uint8_t bLockDelayUnits; ///< Indicates the units used for the wLockDelay field.
+ uint16_t wLockDelay; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry.
+} audio10_desc_cs_as_iso_data_ep_t;
+
+/// AUDIO Interrupt Data Message Format UAC1 (3.7.1.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bStatusType;///< Indicates the type of status information being reported.
+ uint8_t bOriginator;///< Indicates the entity that originated this status information.
+} audio10_interrupt_data_t;
+
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 2.0 (UAC2) DEFINITIONS
+//--------------------------------------------------------------------+
+
/// A.7 - Audio Function Category Codes
-typedef enum
-{
- AUDIO_FUNC_UNDEF = 0x00,
- AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
- AUDIO_FUNC_HOME_THEATER = 0x02,
- AUDIO_FUNC_MICROPHONE = 0x03,
- AUDIO_FUNC_HEADSET = 0x04,
- AUDIO_FUNC_TELEPHONE = 0x05,
- AUDIO_FUNC_CONVERTER = 0x06,
- AUDIO_FUNC_SOUND_RECODER = 0x07,
- AUDIO_FUNC_IO_BOX = 0x08,
- AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
- AUDIO_FUNC_PRO_AUDIO = 0x0A,
- AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
- AUDIO_FUNC_CONTROL_PANEL = 0x0C,
- AUDIO_FUNC_OTHER = 0xFF,
-} audio_function_code_t;
+typedef enum {
+ AUDIO20_FUNC_UNDEF = 0x00,
+ AUDIO20_FUNC_DESKTOP_SPEAKER = 0x01,
+ AUDIO20_FUNC_HOME_THEATER = 0x02,
+ AUDIO20_FUNC_MICROPHONE = 0x03,
+ AUDIO20_FUNC_HEADSET = 0x04,
+ AUDIO20_FUNC_TELEPHONE = 0x05,
+ AUDIO20_FUNC_CONVERTER = 0x06,
+ AUDIO20_FUNC_SOUND_RECODER = 0x07,
+ AUDIO20_FUNC_IO_BOX = 0x08,
+ AUDIO20_FUNC_MUSICAL_INSTRUMENT = 0x09,
+ AUDIO20_FUNC_PRO_AUDIO = 0x0A,
+ AUDIO20_FUNC_AUDIO_VIDEO = 0x0B,
+ AUDIO20_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO20_FUNC_OTHER = 0xFF,
+} audio20_function_code_t;
/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
-typedef enum
-{
- AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
- AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
- AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
- AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
- AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
- AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
- AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
- AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
- AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
- AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
- AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
- AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
- AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
- AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
-} audio_cs_ac_interface_subtype_t;
+typedef enum {
+ AUDIO20_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO20_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO20_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO20_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO20_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO20_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO20_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO20_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO20_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO20_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO20_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio20_cs_ac_interface_subtype_t;
/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
-typedef enum
-{
- AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
- AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
- AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
- AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
- AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
-} audio_cs_as_interface_subtype_t;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO20_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO20_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO20_CS_AS_INTERFACE_DECODER = 0x04,
+} audio20_cs_as_interface_subtype_t;
/// A.11 - Effect Unit Effect Types
-typedef enum
-{
- AUDIO_EFFECT_TYPE_UNDEF = 0x00,
- AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
- AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
- AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
- AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
-} audio_effect_unit_effect_type_t;
+typedef enum {
+ AUDIO20_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO20_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO20_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO20_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO20_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio20_effect_unit_effect_type_t;
/// A.12 - Processing Unit Process Types
-typedef enum
-{
- AUDIO_PROCESS_TYPE_UNDEF = 0x00,
- AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
- AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
- AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
-} audio_processing_unit_process_type_t;
+typedef enum {
+ AUDIO20_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO20_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO20_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO20_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio20_processing_unit_process_type_t;
/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
-typedef enum
-{
- AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
- AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
-} audio_cs_ep_subtype_t;
+typedef enum {
+ AUDIO20_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO20_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio20_cs_ep_subtype_t;
-/// A.14 - Audio Class-Specific Request Codes
-typedef enum
-{
- AUDIO_CS_REQ_UNDEF = 0x00,
- AUDIO_CS_REQ_CUR = 0x01,
- AUDIO_CS_REQ_RANGE = 0x02,
- AUDIO_CS_REQ_MEM = 0x03,
-} audio_cs_req_t;
+/// A.14 - Audio Class-Specific Request Codes UAC2
+typedef enum {
+ AUDIO20_CS_REQ_UNDEF = 0x00,
+ AUDIO20_CS_REQ_CUR = 0x01,
+ AUDIO20_CS_REQ_RANGE = 0x02,
+ AUDIO20_CS_REQ_MEM = 0x03,
+} audio20_cs_req_t;
-/// A.17 - Control Selector Codes
+/// A.17 - Control Selector Codes UAC2
/// A.17.1 - Clock Source Control Selectors
-typedef enum
-{
- AUDIO_CS_CTRL_UNDEF = 0x00,
- AUDIO_CS_CTRL_SAM_FREQ = 0x01,
- AUDIO_CS_CTRL_CLK_VALID = 0x02,
-} audio_clock_src_control_selector_t;
+typedef enum {
+ AUDIO20_CS_CTRL_UNDEF = 0x00,
+ AUDIO20_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO20_CS_CTRL_CLK_VALID = 0x02,
+} audio20_clock_src_control_selector_t;
/// A.17.2 - Clock Selector Control Selectors
-typedef enum
-{
- AUDIO_CX_CTRL_UNDEF = 0x00,
- AUDIO_CX_CTRL_CONTROL = 0x01,
-} audio_clock_sel_control_selector_t;
+typedef enum {
+ AUDIO20_CX_CTRL_UNDEF = 0x00,
+ AUDIO20_CX_CTRL_CONTROL = 0x01,
+} audio20_clock_sel_control_selector_t;
/// A.17.3 - Clock Multiplier Control Selectors
-typedef enum
-{
- AUDIO_CM_CTRL_UNDEF = 0x00,
- AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
- AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
-} audio_clock_mul_control_selector_t;
+typedef enum {
+ AUDIO20_CM_CTRL_UNDEF = 0x00,
+ AUDIO20_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO20_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio20_clock_mul_control_selector_t;
-/// A.17.4 - Terminal Control Selectors
-typedef enum
-{
- AUDIO_TE_CTRL_UNDEF = 0x00,
- AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
- AUDIO_TE_CTRL_CONNECTOR = 0x02,
- AUDIO_TE_CTRL_OVERLOAD = 0x03,
- AUDIO_TE_CTRL_CLUSTER = 0x04,
- AUDIO_TE_CTRL_UNDERFLOW = 0x05,
- AUDIO_TE_CTRL_OVERFLOW = 0x06,
- AUDIO_TE_CTRL_LATENCY = 0x07,
-} audio_terminal_control_selector_t;
+/// A.17.4 - Terminal Control Selectors UAC2
+typedef enum {
+ AUDIO20_TE_CTRL_UNDEF = 0x00,
+ AUDIO20_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO20_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO20_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO20_TE_CTRL_CLUSTER = 0x04,
+ AUDIO20_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO20_TE_CTRL_LATENCY = 0x07,
+} audio20_terminal_control_selector_t;
/// A.17.5 - Mixer Control Selectors
-typedef enum
-{
- AUDIO_MU_CTRL_UNDEF = 0x00,
- AUDIO_MU_CTRL_MIXER = 0x01,
- AUDIO_MU_CTRL_CLUSTER = 0x02,
- AUDIO_MU_CTRL_UNDERFLOW = 0x03,
- AUDIO_MU_CTRL_OVERFLOW = 0x04,
- AUDIO_MU_CTRL_LATENCY = 0x05,
-} audio_mixer_control_selector_t;
+typedef enum {
+ AUDIO20_MU_CTRL_UNDEF = 0x00,
+ AUDIO20_MU_CTRL_MIXER = 0x01,
+ AUDIO20_MU_CTRL_CLUSTER = 0x02,
+ AUDIO20_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO20_MU_CTRL_LATENCY = 0x05,
+} audio20_mixer_control_selector_t;
/// A.17.6 - Selector Control Selectors
-typedef enum
-{
- AUDIO_SU_CTRL_UNDEF = 0x00,
- AUDIO_SU_CTRL_SELECTOR = 0x01,
- AUDIO_SU_CTRL_LATENCY = 0x02,
-} audio_sel_control_selector_t;
+typedef enum {
+ AUDIO20_SU_CTRL_UNDEF = 0x00,
+ AUDIO20_SU_CTRL_SELECTOR = 0x01,
+ AUDIO20_SU_CTRL_LATENCY = 0x02,
+} audio20_sel_control_selector_t;
-/// A.17.7 - Feature Unit Control Selectors
-typedef enum
-{
- AUDIO_FU_CTRL_UNDEF = 0x00,
- AUDIO_FU_CTRL_MUTE = 0x01,
- AUDIO_FU_CTRL_VOLUME = 0x02,
- AUDIO_FU_CTRL_BASS = 0x03,
- AUDIO_FU_CTRL_MID = 0x04,
- AUDIO_FU_CTRL_TREBLE = 0x05,
- AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
- AUDIO_FU_CTRL_AGC = 0x07,
- AUDIO_FU_CTRL_DELAY = 0x08,
- AUDIO_FU_CTRL_BASS_BOOST = 0x09,
- AUDIO_FU_CTRL_LOUDNESS = 0x0A,
- AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
- AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
- AUDIO_FU_CTRL_INVERTER = 0x0D,
- AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
- AUDIO_FU_CTRL_OVERVLOW = 0x0F,
- AUDIO_FU_CTRL_LATENCY = 0x10,
-} audio_feature_unit_control_selector_t;
+/// A.17.7 - Feature Unit Control Selectors UAC2
+typedef enum {
+ AUDIO20_FU_CTRL_UNDEF = 0x00,
+ AUDIO20_FU_CTRL_MUTE = 0x01,
+ AUDIO20_FU_CTRL_VOLUME = 0x02,
+ AUDIO20_FU_CTRL_BASS = 0x03,
+ AUDIO20_FU_CTRL_MID = 0x04,
+ AUDIO20_FU_CTRL_TREBLE = 0x05,
+ AUDIO20_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO20_FU_CTRL_AGC = 0x07,
+ AUDIO20_FU_CTRL_DELAY = 0x08,
+ AUDIO20_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO20_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO20_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO20_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO20_FU_CTRL_INVERTER = 0x0D,
+ AUDIO20_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO20_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO20_FU_CTRL_LATENCY = 0x10,
+} audio20_feature_unit_control_selector_t;
/// A.17.8 Effect Unit Control Selectors
/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
-typedef enum
-{
- AUDIO_PE_CTRL_UNDEF = 0x00,
- AUDIO_PE_CTRL_ENABLE = 0x01,
- AUDIO_PE_CTRL_CENTERFREQ = 0x02,
- AUDIO_PE_CTRL_QFACTOR = 0x03,
- AUDIO_PE_CTRL_GAIN = 0x04,
- AUDIO_PE_CTRL_UNDERFLOW = 0x05,
- AUDIO_PE_CTRL_OVERFLOW = 0x06,
- AUDIO_PE_CTRL_LATENCY = 0x07,
-} audio_parametric_equalizer_control_selector_t;
+typedef enum {
+ AUDIO20_PE_CTRL_UNDEF = 0x00,
+ AUDIO20_PE_CTRL_ENABLE = 0x01,
+ AUDIO20_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO20_PE_CTRL_QFACTOR = 0x03,
+ AUDIO20_PE_CTRL_GAIN = 0x04,
+ AUDIO20_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO20_PE_CTRL_LATENCY = 0x07,
+} audio20_parametric_equalizer_control_selector_t;
/// A.17.8.2 Reverberation Effect Unit Control Selectors
-typedef enum
-{
- AUDIO_RV_CTRL_UNDEF = 0x00,
- AUDIO_RV_CTRL_ENABLE = 0x01,
- AUDIO_RV_CTRL_TYPE = 0x02,
- AUDIO_RV_CTRL_LEVEL = 0x03,
- AUDIO_RV_CTRL_TIME = 0x04,
- AUDIO_RV_CTRL_FEEDBACK = 0x05,
- AUDIO_RV_CTRL_PREDELAY = 0x06,
- AUDIO_RV_CTRL_DENSITY = 0x07,
- AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
- AUDIO_RV_CTRL_UNDERFLOW = 0x09,
- AUDIO_RV_CTRL_OVERFLOW = 0x0A,
- AUDIO_RV_CTRL_LATENCY = 0x0B,
-} audio_reverberation_effect_control_selector_t;
+typedef enum {
+ AUDIO20_RV_CTRL_UNDEF = 0x00,
+ AUDIO20_RV_CTRL_ENABLE = 0x01,
+ AUDIO20_RV_CTRL_TYPE = 0x02,
+ AUDIO20_RV_CTRL_LEVEL = 0x03,
+ AUDIO20_RV_CTRL_TIME = 0x04,
+ AUDIO20_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO20_RV_CTRL_PREDELAY = 0x06,
+ AUDIO20_RV_CTRL_DENSITY = 0x07,
+ AUDIO20_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO20_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO20_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO20_RV_CTRL_LATENCY = 0x0B,
+} audio20_reverberation_effect_control_selector_t;
/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
-typedef enum
-{
- AUDIO_MD_CTRL_UNDEF = 0x00,
- AUDIO_MD_CTRL_ENABLE = 0x01,
- AUDIO_MD_CTRL_BALANCE = 0x02,
- AUDIO_MD_CTRL_RATE = 0x03,
- AUDIO_MD_CTRL_DEPTH = 0x04,
- AUDIO_MD_CTRL_TIME = 0x05,
- AUDIO_MD_CTRL_FEEDBACK = 0x06,
- AUDIO_MD_CTRL_UNDERFLOW = 0x07,
- AUDIO_MD_CTRL_OVERFLOW = 0x08,
- AUDIO_MD_CTRL_LATENCY = 0x09,
-} audio_modulation_delay_control_selector_t;
+typedef enum {
+ AUDIO20_MD_CTRL_UNDEF = 0x00,
+ AUDIO20_MD_CTRL_ENABLE = 0x01,
+ AUDIO20_MD_CTRL_BALANCE = 0x02,
+ AUDIO20_MD_CTRL_RATE = 0x03,
+ AUDIO20_MD_CTRL_DEPTH = 0x04,
+ AUDIO20_MD_CTRL_TIME = 0x05,
+ AUDIO20_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO20_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO20_MD_CTRL_LATENCY = 0x09,
+} audio20_modulation_delay_control_selector_t;
/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
-typedef enum
-{
- AUDIO_DR_CTRL_UNDEF = 0x00,
- AUDIO_DR_CTRL_ENABLE = 0x01,
- AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
- AUDIO_DR_CTRL_MAXAMPL = 0x03,
- AUDIO_DR_CTRL_THRESHOLD = 0x04,
- AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
- AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
- AUDIO_DR_CTRL_UNDERFLOW = 0x07,
- AUDIO_DR_CTRL_OVERFLOW = 0x08,
- AUDIO_DR_CTRL_LATENCY = 0x09,
-} audio_dynamic_range_compression_control_selector_t;
+typedef enum {
+ AUDIO20_DR_CTRL_UNDEF = 0x00,
+ AUDIO20_DR_CTRL_ENABLE = 0x01,
+ AUDIO20_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO20_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO20_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO20_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO20_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO20_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO20_DR_CTRL_LATENCY = 0x09,
+} audio20_dynamic_range_compression_control_selector_t;
/// A.17.9 Processing Unit Control Selectors
/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
-typedef enum
-{
- AUDIO_UD_CTRL_UNDEF = 0x00,
- AUDIO_UD_CTRL_ENABLE = 0x01,
- AUDIO_UD_CTRL_MODE_SELECT = 0x02,
- AUDIO_UD_CTRL_CLUSTER = 0x03,
- AUDIO_UD_CTRL_UNDERFLOW = 0x04,
- AUDIO_UD_CTRL_OVERFLOW = 0x05,
- AUDIO_UD_CTRL_LATENCY = 0x06,
-} audio_up_down_mix_control_selector_t;
+typedef enum {
+ AUDIO20_UD_CTRL_UNDEF = 0x00,
+ AUDIO20_UD_CTRL_ENABLE = 0x01,
+ AUDIO20_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO20_UD_CTRL_CLUSTER = 0x03,
+ AUDIO20_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO20_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO20_UD_CTRL_LATENCY = 0x06,
+} audio20_up_down_mix_control_selector_t;
/// A.17.9.2 Dolby Prologic â„¢ Processing Unit Control Selectors
-typedef enum
-{
- AUDIO_DP_CTRL_UNDEF = 0x00,
- AUDIO_DP_CTRL_ENABLE = 0x01,
- AUDIO_DP_CTRL_MODE_SELECT = 0x02,
- AUDIO_DP_CTRL_CLUSTER = 0x03,
- AUDIO_DP_CTRL_UNDERFLOW = 0x04,
- AUDIO_DP_CTRL_OVERFLOW = 0x05,
- AUDIO_DP_CTRL_LATENCY = 0x06,
-} audio_dolby_prologic_control_selector_t;
+typedef enum {
+ AUDIO20_DP_CTRL_UNDEF = 0x00,
+ AUDIO20_DP_CTRL_ENABLE = 0x01,
+ AUDIO20_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO20_DP_CTRL_CLUSTER = 0x03,
+ AUDIO20_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO20_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO20_DP_CTRL_LATENCY = 0x06,
+} audio20_dolby_prologic_control_selector_t;
/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
-typedef enum
-{
- AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
- AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
- AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
- AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
- AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
- AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
-} audio_stereo_extender_control_selector_t;
+typedef enum {
+ AUDIO20_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO20_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO20_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO20_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO20_ST_EXT_CTRL_LATENCY = 0x05,
+} audio20_stereo_extender_control_selector_t;
/// A.17.10 Extension Unit Control Selectors
-typedef enum
-{
- AUDIO_XU_CTRL_UNDEF = 0x00,
- AUDIO_XU_CTRL_ENABLE = 0x01,
- AUDIO_XU_CTRL_CLUSTER = 0x02,
- AUDIO_XU_CTRL_UNDERFLOW = 0x03,
- AUDIO_XU_CTRL_OVERFLOW = 0x04,
- AUDIO_XU_CTRL_LATENCY = 0x05,
-} audio_extension_unit_control_selector_t;
+typedef enum {
+ AUDIO20_XU_CTRL_UNDEF = 0x00,
+ AUDIO20_XU_CTRL_ENABLE = 0x01,
+ AUDIO20_XU_CTRL_CLUSTER = 0x02,
+ AUDIO20_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO20_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO20_XU_CTRL_LATENCY = 0x05,
+} audio20_extension_unit_control_selector_t;
/// A.17.11 AudioStreaming Interface Control Selectors
-typedef enum
-{
- AUDIO_AS_CTRL_UNDEF = 0x00,
- AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
- AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
- AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
-} audio_audiostreaming_interface_control_selector_t;
+typedef enum {
+ AUDIO20_AS_CTRL_UNDEF = 0x00,
+ AUDIO20_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO20_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO20_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio20_audiostreaming_interface_control_selector_t;
/// A.17.12 Encoder Control Selectors
-typedef enum
-{
- AUDIO_EN_CTRL_UNDEF = 0x00,
- AUDIO_EN_CTRL_BIT_RATE = 0x01,
- AUDIO_EN_CTRL_QUALITY = 0x02,
- AUDIO_EN_CTRL_VBR = 0x03,
- AUDIO_EN_CTRL_TYPE = 0x04,
- AUDIO_EN_CTRL_UNDERFLOW = 0x05,
- AUDIO_EN_CTRL_OVERFLOW = 0x06,
- AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
- AUDIO_EN_CTRL_PARAM1 = 0x08,
- AUDIO_EN_CTRL_PARAM2 = 0x09,
- AUDIO_EN_CTRL_PARAM3 = 0x0A,
- AUDIO_EN_CTRL_PARAM4 = 0x0B,
- AUDIO_EN_CTRL_PARAM5 = 0x0C,
- AUDIO_EN_CTRL_PARAM6 = 0x0D,
- AUDIO_EN_CTRL_PARAM7 = 0x0E,
- AUDIO_EN_CTRL_PARAM8 = 0x0F,
-} audio_encoder_control_selector_t;
+typedef enum {
+ AUDIO20_EN_CTRL_UNDEF = 0x00,
+ AUDIO20_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO20_EN_CTRL_QUALITY = 0x02,
+ AUDIO20_EN_CTRL_VBR = 0x03,
+ AUDIO20_EN_CTRL_TYPE = 0x04,
+ AUDIO20_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO20_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO20_EN_CTRL_PARAM1 = 0x08,
+ AUDIO20_EN_CTRL_PARAM2 = 0x09,
+ AUDIO20_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO20_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO20_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO20_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO20_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO20_EN_CTRL_PARAM8 = 0x0F,
+} audio20_encoder_control_selector_t;
/// A.17.13 Decoder Control Selectors
/// A.17.13.1 MPEG Decoder Control Selectors
-typedef enum
-{
- AUDIO_MPD_CTRL_UNDEF = 0x00,
- AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
- AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
- AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
- AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
- AUDIO_MPD_CTRL_SCALING = 0x05,
- AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
- AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
- AUDIO_MPD_CTRL_OVERFLOW = 0x08,
- AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
-} audio_MPEG_decoder_control_selector_t;
+typedef enum {
+ AUDIO20_MPD_CTRL_UNDEF = 0x00,
+ AUDIO20_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO20_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO20_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO20_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO20_MPD_CTRL_SCALING = 0x05,
+ AUDIO20_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO20_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO20_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO20_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio20_MPEG_decoder_control_selector_t;
/// A.17.13.2 AC-3 Decoder Control Selectors
-typedef enum
-{
- AUDIO_AD_CTRL_UNDEF = 0x00,
- AUDIO_AD_CTRL_MODE = 0x01,
- AUDIO_AD_CTRL_DYN_RANGE = 0x02,
- AUDIO_AD_CTRL_SCALING = 0x03,
- AUDIO_AD_CTRL_HILO_SCALING = 0x04,
- AUDIO_AD_CTRL_UNDERFLOW = 0x05,
- AUDIO_AD_CTRL_OVERFLOW = 0x06,
- AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
-} audio_AC3_decoder_control_selector_t;
+typedef enum {
+ AUDIO20_AD_CTRL_UNDEF = 0x00,
+ AUDIO20_AD_CTRL_MODE = 0x01,
+ AUDIO20_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO20_AD_CTRL_SCALING = 0x03,
+ AUDIO20_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO20_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO20_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO20_AD_CTRL_DECODER_ERROR = 0x07,
+} audio20_AC3_decoder_control_selector_t;
/// A.17.13.3 WMA Decoder Control Selectors
-typedef enum
-{
- AUDIO_WD_CTRL_UNDEF = 0x00,
- AUDIO_WD_CTRL_UNDERFLOW = 0x01,
- AUDIO_WD_CTRL_OVERFLOW = 0x02,
- AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
-} audio_WMA_decoder_control_selector_t;
+typedef enum {
+ AUDIO20_WD_CTRL_UNDEF = 0x00,
+ AUDIO20_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO20_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO20_WD_CTRL_DECODER_ERROR = 0x03,
+} audio20_WMA_decoder_control_selector_t;
/// A.17.13.4 DTS Decoder Control Selectors
-typedef enum
-{
- AUDIO_DD_CTRL_UNDEF = 0x00,
- AUDIO_DD_CTRL_UNDERFLOW = 0x01,
- AUDIO_DD_CTRL_OVERFLOW = 0x02,
- AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
-} audio_DTS_decoder_control_selector_t;
+typedef enum {
+ AUDIO20_DD_CTRL_UNDEF = 0x00,
+ AUDIO20_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO20_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO20_DD_CTRL_DECODER_ERROR = 0x03,
+} audio20_DTS_decoder_control_selector_t;
/// A.17.14 Endpoint Control Selectors
-typedef enum
-{
- AUDIO_EP_CTRL_UNDEF = 0x00,
- AUDIO_EP_CTRL_PITCH = 0x01,
- AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
- AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
-} audio_EP_control_selector_t;
-
-/// Terminal Types
-
-/// 2.1 - Audio Class-Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
- AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
- AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
-} audio_terminal_type_t;
-
-/// 2.2 - Audio Class-Input Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
- AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
- AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
- AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
- AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
- AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
- AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
-} audio_terminal_input_type_t;
-
-/// 2.3 - Audio Class-Output Terminal Types UAC2
-typedef enum
-{
- AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
- AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
- AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
- AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
- AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
- AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
- AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
- AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
-} audio_terminal_output_type_t;
-
-/// Rest is yet to be implemented
+typedef enum {
+ AUDIO20_EP_CTRL_UNDEF = 0x00,
+ AUDIO20_EP_CTRL_PITCH = 0x01,
+ AUDIO20_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO20_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio20_EP_control_selector_t;
/// Additional Audio Device Class Codes - Source: Audio Data Formats
/// A.1 - Audio Class-Format Type Codes UAC2
-typedef enum
-{
- AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
- AUDIO_FORMAT_TYPE_I = 0x01,
- AUDIO_FORMAT_TYPE_II = 0x02,
- AUDIO_FORMAT_TYPE_III = 0x03,
- AUDIO_FORMAT_TYPE_IV = 0x04,
- AUDIO_EXT_FORMAT_TYPE_I = 0x81,
- AUDIO_EXT_FORMAT_TYPE_II = 0x82,
- AUDIO_EXT_FORMAT_TYPE_III = 0x83,
-} audio_format_type_t;
+typedef enum {
+ AUDIO20_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO20_FORMAT_TYPE_I = 0x01,
+ AUDIO20_FORMAT_TYPE_II = 0x02,
+ AUDIO20_FORMAT_TYPE_III = 0x03,
+ AUDIO20_FORMAT_TYPE_IV = 0x04,
+ AUDIO20_EXT_FORMAT_TYPE_I = 0x81,
+ AUDIO20_EXT_FORMAT_TYPE_II = 0x82,
+ AUDIO20_EXT_FORMAT_TYPE_III = 0x83,
+} audio20_format_type_t;
// A.2.1 - Audio Class-Audio Data Format Type I UAC2
-typedef enum
-{
- AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
- AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
- AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
- AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
- AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
- AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
-} audio_data_format_type_I_t;
+typedef enum {
+ AUDIO20_DATA_FORMAT_TYPE_I_PCM = 1 << 0,
+ AUDIO20_DATA_FORMAT_TYPE_I_PCM8 = 1 << 1,
+ AUDIO20_DATA_FORMAT_TYPE_I_IEEE_FLOAT = 1 << 2,
+ AUDIO20_DATA_FORMAT_TYPE_I_ALAW = 1 << 3,
+ AUDIO20_DATA_FORMAT_TYPE_I_MULAW = 1 << 4,
+ AUDIO20_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
+} audio20_data_format_type_I_t;
+
+/// Audio Class-Audio Channel Configuration UAC2 (Table A-11)
+typedef enum {
+ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO20_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO20_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO20_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO20_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO20_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO20_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO20_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO20_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO20_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO20_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO20_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO20_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO20_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO20_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO20_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO20_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO20_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO20_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO20_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO20_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO20_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO20_CHANNEL_CONFIG_RAW_DATA = 0x80000000u,
+} audio20_channel_config_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
/// Audio Class-Control Values UAC2
-typedef enum
-{
- AUDIO_CTRL_NONE = 0x00, ///< No Host access
- AUDIO_CTRL_R = 0x01, ///< Host read access only
- AUDIO_CTRL_RW = 0x03, ///< Host read write access
-} audio_control_t;
+typedef enum {
+ AUDIO20_CTRL_NONE = 0x00,///< No Host access
+ AUDIO20_CTRL_R = 0x01, ///< Host read access only
+ AUDIO20_CTRL_RW = 0x03, ///< Host read write access
+} audio20_control_t;
/// Audio Class-Specific AC Interface Descriptor Controls UAC2
-typedef enum
-{
- AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
-} audio_cs_ac_interface_control_pos_t;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio20_cs_ac_interface_control_pos_t;
/// Audio Class-Specific AS Interface Descriptor Controls UAC2
-typedef enum
-{
- AUDIO_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
- AUDIO_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
-} audio_cs_as_interface_control_pos_t;
+typedef enum {
+ AUDIO20_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO20_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio20_cs_as_interface_control_pos_t;
/// Audio Class-Specific AS Isochronous Data EP Attributes UAC2
-typedef enum
-{
- AUDIO_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
- AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
-} audio_cs_as_iso_data_ep_attribute_t;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio20_cs_as_iso_data_ep_attribute_t;
/// Audio Class-Specific AS Isochronous Data EP Controls UAC2
-typedef enum
-{
- AUDIO_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
- AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
- AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
-} audio_cs_as_iso_data_ep_control_pos_t;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO20_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio20_cs_as_iso_data_ep_control_pos_t;
/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC2
-typedef enum
-{
- AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
- AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
- AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
-} audio_cs_as_iso_data_ep_lock_delay_unit_t;
+typedef enum {
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio20_cs_as_iso_data_ep_lock_delay_unit_t;
/// Audio Class-Clock Source Attributes UAC2
-typedef enum
-{
- AUDIO_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
- AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
- AUDIO_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
- AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
- AUDIO_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
-} audio_clock_source_attribute_t;
+typedef enum {
+ AUDIO20_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO20_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO20_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio20_clock_source_attribute_t;
/// Audio Class-Clock Source Controls UAC2
-typedef enum
-{
- AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
- AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
-} audio_clock_source_control_pos_t;
+typedef enum {
+ AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO20_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio20_clock_source_control_pos_t;
/// Audio Class-Clock Selector Controls UAC2
-typedef enum
-{
- AUDIO_CLOCK_SELECTOR_CTRL_POS = 0,
-} audio_clock_selector_control_pos_t;
+typedef enum {
+ AUDIO20_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio20_clock_selector_control_pos_t;
/// Audio Class-Clock Multiplier Controls UAC2
-typedef enum
-{
- AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
- AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
-} audio_clock_multiplier_control_pos_t;
+typedef enum {
+ AUDIO20_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO20_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio20_clock_multiplier_control_pos_t;
/// Audio Class-Input Terminal Controls UAC2
-typedef enum
-{
- AUDIO_IN_TERM_CTRL_CPY_PROT_POS = 0,
- AUDIO_IN_TERM_CTRL_CONNECTOR_POS = 2,
- AUDIO_IN_TERM_CTRL_OVERLOAD_POS = 4,
- AUDIO_IN_TERM_CTRL_CLUSTER_POS = 6,
- AUDIO_IN_TERM_CTRL_UNDERFLOW_POS = 8,
- AUDIO_IN_TERM_CTRL_OVERFLOW_POS = 10,
-} audio_terminal_input_control_pos_t;
+typedef enum {
+ AUDIO20_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO20_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO20_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO20_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO20_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO20_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio20_terminal_input_control_pos_t;
/// Audio Class-Output Terminal Controls UAC2
-typedef enum
-{
- AUDIO_OUT_TERM_CTRL_CPY_PROT_POS = 0,
- AUDIO_OUT_TERM_CTRL_CONNECTOR_POS = 2,
- AUDIO_OUT_TERM_CTRL_OVERLOAD_POS = 4,
- AUDIO_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
- AUDIO_OUT_TERM_CTRL_OVERFLOW_POS = 8,
-} audio_terminal_output_control_pos_t;
+typedef enum {
+ AUDIO20_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO20_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO20_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO20_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO20_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio20_terminal_output_control_pos_t;
/// Audio Class-Feature Unit Controls UAC2
-typedef enum
-{
- AUDIO_FEATURE_UNIT_CTRL_MUTE_POS = 0,
- AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
- AUDIO_FEATURE_UNIT_CTRL_BASS_POS = 4,
- AUDIO_FEATURE_UNIT_CTRL_MID_POS = 6,
- AUDIO_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
- AUDIO_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
- AUDIO_FEATURE_UNIT_CTRL_AGC_POS = 12,
- AUDIO_FEATURE_UNIT_CTRL_DELAY_POS = 14,
- AUDIO_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
- AUDIO_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
- AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
- AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
- AUDIO_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
- AUDIO_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
- AUDIO_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
-} audio_feature_unit_control_pos_t;
+typedef enum {
+ AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO20_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO20_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO20_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO20_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO20_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO20_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO20_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO20_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO20_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO20_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO20_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO20_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO20_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio20_feature_unit_control_pos_t;
-/// Audio Class-Audio Channel Configuration UAC2
-typedef enum
-{
- AUDIO_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
- AUDIO_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
- AUDIO_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
- AUDIO_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
- AUDIO_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
- AUDIO_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
- AUDIO_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
- AUDIO_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
- AUDIO_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
- AUDIO_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
- AUDIO_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
- AUDIO_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
- AUDIO_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
- AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
- AUDIO_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
- AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
- AUDIO_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
- AUDIO_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
- AUDIO_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
- AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
- AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
- AUDIO_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
- AUDIO_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
- AUDIO_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
- AUDIO_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
- AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
- AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
- AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
- AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000u,
-} audio_channel_config_t;
+//--------------------------------------------------------------------+
+// USB AUDIO CLASS 2.0 (UAC2) DESCRIPTORS
+//--------------------------------------------------------------------+
-/// AUDIO Channel Cluster Descriptor (4.1)
+/// AUDIO Channel Cluster Descriptor UAC2 (4.1)
typedef struct TU_ATTR_PACKED {
- uint8_t bNrChannels; ///< Number of channels currently connected.
- audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
- uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
-} audio_desc_channel_cluster_t;
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ uint32_t bmChannelConfig;///< Bitmap according to 'audio20_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
+} audio20_desc_channel_cluster_t;
-/// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes: 9.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER.
- uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
- uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t.
- uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
- uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
-} audio_desc_cs_ac_interface_t;
-TU_VERIFY_STATIC(sizeof(audio_desc_cs_ac_interface_t) == 9, "size is not correct");
+/// AUDIO Class-Specific AC Interface Header Descriptor UAC2 (4.7.2)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_HEADER.
+ uint16_t bcdADC; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
+ uint8_t bCategory; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio20_function_code_t.
+ uint16_t wTotalLength; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
+ uint8_t bmControls; ///< See: audio20_cs_ac_interface_control_pos_t.
+} audio20_desc_cs_ac_interface_t;
+TU_VERIFY_STATIC(sizeof(audio20_desc_cs_ac_interface_t) == 9, "size is not correct");
-/// AUDIO Clock Source Descriptor (4.7.2.1)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
- uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
- uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
- uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
- uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
- uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
-} audio_desc_clock_source_t;
+/// AUDIO Clock Source Descriptor UAC2 (4.7.2.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor in bytes: 8.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_CLOCK_SOURCE.
+ uint8_t bClockID; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bmAttributes; ///< See: audio20_clock_source_attribute_t.
+ uint8_t bmControls; ///< See: audio20_clock_source_control_pos_t.
+ uint8_t bAssocTerminal; ///< Terminal ID of the Terminal that is associated with this Clock Source.
+ uint8_t iClockSource; ///< Index of a string descriptor, describing the Clock Source Entity.
+} audio20_desc_clock_source_t;
-/// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR.
- uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
- uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
- uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
- uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t.
- uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity.
-} audio_desc_clock_selector_t;
+/// AUDIO Clock Selector Descriptor UAC2 (4.7.2.2) for ONE pin
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 7+p.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_CLOCK_SELECTOR.
+ uint8_t bClockID; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bNrInPins; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
+ uint8_t baCSourceID; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
+ uint8_t bmControls; ///< See: audio20_clock_selector_control_pos_t.
+ uint8_t iClockSource; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio20_desc_clock_selector_t;
/// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins
-#define audio_desc_clock_selector_n_t(source_num) \
- struct TU_ATTR_PACKED { \
- uint8_t bLength ; \
- uint8_t bDescriptorType ; \
- uint8_t bDescriptorSubType ; \
- uint8_t bClockID ; \
- uint8_t bNrInPins ; \
- struct TU_ATTR_PACKED { \
- uint8_t baSourceID ; \
- } sourceID[source_num] ; \
- uint8_t bmControls ; \
- uint8_t iClockSource ; \
-}
+#define audio20_desc_clock_selector_n_t(source_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bClockID; \
+ uint8_t bNrInPins; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID; \
+ } sourceID[source_num]; \
+ uint8_t bmControls; \
+ uint8_t iClockSource; \
+ }
-/// AUDIO Clock Multiplier Descriptor (4.7.2.3)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 7.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
- uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
- uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
- uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t.
- uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
-} audio_desc_clock_multiplier_t;
+/// AUDIO Clock Multiplier Descriptor UAC2 (4.7.2.3)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 7.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
+ uint8_t bClockID; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bCSourceID; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
+ uint8_t bmControls; ///< See: audio20_clock_multiplier_control_pos_t.
+ uint8_t iClockSource; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio20_desc_clock_multiplier_t;
/// AUDIO Input Terminal Descriptor(4.7.2.4)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
- uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this terminal.
- uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
- uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
- uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
- uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
- uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
- uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first logical channel.
- uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
- uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
-} audio_desc_input_terminal_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 17.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this terminal.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
+ uint8_t bAssocTerminal; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bCSourceID; ///< ID of the Clock Entity to which this Input Terminal is connected.
+ uint8_t bNrChannels; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
+ uint32_t bmChannelConfig; ///< Describes the spatial location of the logical channels. See:audio20_channel_config_t.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first logical channel.
+ uint16_t bmControls; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Input Terminal.
+} audio20_desc_input_terminal_t;
-/// AUDIO Output Terminal Descriptor(4.7.2.5)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 12.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL.
- uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
- uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types.
- uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
- uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected.
- uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected.
- uint16_t bmControls ; ///< See: audio_terminal_output_type_t.
- uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal.
-} audio_desc_output_terminal_t;
+/// AUDIO Output Terminal Descriptor UAC2 (4.7.2.5)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 12.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
+ uint16_t wTerminalType; ///< Constant characterizing the type of Terminal. See: audio20_terminal_type_t for USB streaming and audio20_terminal_output_type_t for other output types.
+ uint8_t bAssocTerminal; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t bCSourceID; ///< ID of the Clock Entity to which this Output Terminal is connected.
+ uint16_t bmControls; ///< See: audio20_terminal_output_control_pos_t.
+ uint8_t iTerminal; ///< Index of a string descriptor, describing the Output Terminal.
+} audio20_desc_output_terminal_t;
-/// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 14.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT.
- uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
- uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
+/// AUDIO Feature Unit Descriptor UAC2 (4.7.2.8) for ONE channel
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 14.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AC_INTERFACE_FEATURE_UNIT.
+ uint8_t bUnitID; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
+ uint8_t bSourceID; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
struct TU_ATTR_PACKED {
- uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
- } controls[2] ;
- uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit.
-} audio_desc_feature_unit_t;
+ uint32_t bmaControls;///< See: audio20_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
+ } controls[2];
+ uint8_t iTerminal;///< Index of a string descriptor, describing this Feature Unit.
+} audio20_desc_feature_unit_t;
/// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels
-#define audio_desc_feature_unit_n_t(ch_num)\
- struct TU_ATTR_PACKED { \
- uint8_t bLength ; /* 6+(ch_num+1)*4 */\
- uint8_t bDescriptorType ; \
- uint8_t bDescriptorSubType ; \
- uint8_t bUnitID ; \
- uint8_t bSourceID ; \
- struct TU_ATTR_PACKED { \
- uint32_t bmaControls ; \
- } controls[ch_num+1] ; \
- uint8_t iTerminal ; \
-}
+#define audio20_desc_feature_unit_n_t(ch_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; /* 6+(ch_num+1)*4 */ \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bUnitID; \
+ uint8_t bSourceID; \
+ struct TU_ATTR_PACKED { \
+ uint32_t bmaControls; \
+ } controls[ch_num + 1]; \
+ uint8_t iTerminal; \
+ }
/// AUDIO Class-Specific AS Interface Descriptor(4.9.2)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 16.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL.
- uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected.
- uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t.
- uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t.
- uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t.
- uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster.
- uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t.
- uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel.
-} audio_desc_cs_as_interface_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 16.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls; ///< See: audio20_cs_as_interface_control_pos_t.
+ uint8_t bFormatType; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio20_format_type_t.
+ uint32_t bmFormats; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio20_data_format_type_I_t.
+ uint8_t bNrChannels; ///< Number of physical channels in the AS Interface audio channel cluster.
+ uint32_t bmChannelConfig; ///< Describes the spatial location of the physical channels. See: audio20_channel_config_t.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio20_desc_cs_as_interface_t;
/// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 6.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE.
- uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I.
- uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
- uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot.
-} audio_desc_type_I_format_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 6.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO20_FORMAT_TYPE_I.
+ uint8_t bSubslotSize; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
+ uint8_t bBitResolution; ///< The number of effectively used bits from the available bits in an audio subslot.
+} audio20_desc_type_I_format_t;
/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
- uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
- uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
- uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
- uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
- uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
- uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
-} audio_desc_cs_as_iso_data_ep_t;
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength; ///< Size of this descriptor, in bytes: 8.
+ uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType;///< Descriptor SubType. Value: AUDIO20_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes; ///< See: audio20_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls; ///< See: audio20_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits; ///< Indicates the units used for the wLockDelay field. See: audio20_cs_as_iso_data_ep_lock_delay_unit_t.
+ uint16_t wLockDelay; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
+} audio20_desc_cs_as_iso_data_ep_t;
// 5.2.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;
+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 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;
+ 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;
+} audio20_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;
+typedef struct TU_ATTR_PACKED {
+ int8_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_control_cur_1_t;
// 5.2.3.2 2-byte Control CUR Parameter Block
-typedef struct TU_ATTR_PACKED
-{
- int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control
-} audio_control_cur_2_t;
-
-// 5.2.3.3 4-byte Control CUR Parameter Block
-typedef struct TU_ATTR_PACKED
-{
- int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control
-} audio_control_cur_4_t;
-
-// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like
-// 5.2.3.1 1-byte Control RANGE Parameter Block
typedef struct TU_ATTR_PACKED {
- uint16_t wNumSubRanges;
- struct TU_ATTR_PACKED {
- int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
- int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
- uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
- } subrange[] ;
-} audio_control_range_1_t;
+ int16_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_control_cur_2_t;
-// 5.2.3.2 2-byte Control RANGE Parameter Block
+// 5.2.3.3 4-byte Control CUR Parameter Block
typedef struct TU_ATTR_PACKED {
- uint16_t wNumSubRanges;
- struct TU_ATTR_PACKED {
- int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
- int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
- uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
- } subrange[] ;
-} audio_control_range_2_t;
+ int32_t bCur;///< The setting for the CUR attribute of the addressed Control
+} audio20_control_cur_4_t;
-// 5.2.3.3 4-byte Control RANGE Parameter Block
-typedef struct TU_ATTR_PACKED {
- uint16_t wNumSubRanges;
- struct TU_ATTR_PACKED {
- int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
- int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
- uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
- } subrange[] ;
-} audio_control_range_4_t;
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the #define macros below for predefined lengths.
// 5.2.3.1 1-byte Control RANGE Parameter Block
-#define audio_control_range_1_n_t(numSubRanges) \
- struct TU_ATTR_PACKED { \
- uint16_t wNumSubRanges; \
- struct TU_ATTR_PACKED { \
- int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
- int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
- uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
- } subrange[numSubRanges] ; \
-}
+#define audio20_control_range_1_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int8_t bMin; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ \
+ int8_t bMax; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ \
+ uint8_t bRes; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ \
+ } subrange[numSubRanges]; \
+ }
/// 5.2.3.2 2-byte Control RANGE Parameter Block
-#define audio_control_range_2_n_t(numSubRanges) \
- struct TU_ATTR_PACKED { \
- uint16_t wNumSubRanges; \
- struct TU_ATTR_PACKED { \
- int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
- int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
- uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
- } subrange[numSubRanges]; \
-}
+#define audio20_control_range_2_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int16_t bMin; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ \
+ int16_t bMax; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ \
+ uint16_t bRes; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ \
+ } subrange[numSubRanges]; \
+ }
// 5.2.3.3 4-byte Control RANGE Parameter Block
-#define audio_control_range_4_n_t(numSubRanges) \
- struct TU_ATTR_PACKED { \
- uint16_t wNumSubRanges; \
- struct TU_ATTR_PACKED { \
- int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
- int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
- uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
- } subrange[numSubRanges]; \
-}
+#define audio20_control_range_4_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int32_t bMin; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/ \
+ int32_t bMax; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/ \
+ uint32_t bRes; /*The setting for the RES attribute of the nth subrange of the addressed Control*/ \
+ } subrange[numSubRanges]; \
+ }
// 6.1 Interrupt Data Message Format
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bInfo;
uint8_t bAttribute;
- union
- {
+ union {
uint16_t wValue;
- struct
- {
+ struct {
uint8_t wValue_cn_or_mcn;
uint8_t wValue_cs;
};
};
- union
- {
+ union {
uint16_t wIndex;
- struct
- {
+ struct {
uint8_t wIndex_ep_or_int;
uint8_t wIndex_entity_id;
};
};
+} audio20_interrupt_data_t;
+
+//--------------------------------------------------------------------+
+// APPLICATION HELPER DEFINITIONS
+//--------------------------------------------------------------------+
+
+// Combined Interrupt Data Message Format for both UAC1 and UAC2
+typedef union {
+ audio10_interrupt_data_t v1;
+ audio20_interrupt_data_t v2;
} audio_interrupt_data_t;
+// MIDI1.0 use the same CS AC Interface Descriptor as UAC1
+typedef audio10_desc_cs_ac_interface_n_t(1) midi10_desc_cs_ac_interface_t;
+
+// UAC1.0 AC Interface Descriptor with 1 interface, used to read fields other than baInterfaceNr
+typedef audio10_desc_cs_ac_interface_n_t(1) audio10_desc_cs_ac_interface_1_t;
+
/** @} */
#ifdef __cplusplus
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 7a6fd453f..4a8a60192 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -79,10 +79,9 @@
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
// is available or driver is would need to be changed dramatically
-// Only STM32 and dcd_transdimension use non-linear buffer for now
-// dwc2 except esp32sx (since it may use dcd_esp32sx)
+// Only STM32 and ChipIdea HS use non-linear buffer for now
// Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line
-#if (defined(TUP_USBIP_DWC2) && !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)) || \
+#if defined(TUP_USBIP_DWC2) || \
defined(TUP_USBIP_FSDEV) || \
CFG_TUSB_MCU == OPT_MCU_RX63X || \
CFG_TUSB_MCU == OPT_MCU_RX65X || \
@@ -108,19 +107,19 @@
#endif
// Put swap buffer in USB section only if necessary
-#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING
+#if USE_LINEAR_BUFFER
#define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING
+#if USE_LINEAR_BUFFER
#define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-// EP IN software buffers and mutexes
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+// EP IN software buffers
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
tu_static IN_SW_BUF_MEM_ATTR struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ);
@@ -132,24 +131,11 @@ tu_static IN_SW_BUF_MEM_ATTR struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ);
#endif
} ep_in_sw_buf;
-
- #if CFG_FIFO_MUTEX
- #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_in_ff_mutex_wr_1;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_in_ff_mutex_wr_2;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_in_ff_mutex_wr_3;
- #endif
- #endif
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Linear buffer TX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX);
@@ -161,10 +147,10 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX);
#endif
} lin_buf_in;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
-// EP OUT software buffers and mutexes
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+// EP OUT software buffers
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_static OUT_SW_BUF_MEM_ATTR struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ);
@@ -176,24 +162,11 @@ tu_static OUT_SW_BUF_MEM_ATTR struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ);
#endif
} ep_out_sw_buf;
-
- #if CFG_FIFO_MUTEX
- #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_out_ff_mutex_rd_1;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_out_ff_mutex_rd_2;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
- tu_static osal_mutex_def_t ep_out_ff_mutex_rd_3;
- #endif
- #endif
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Linear buffer RX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX);
@@ -205,82 +178,10 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX);
#endif
} lin_buf_out;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
-// Control buffers
-tu_static CFG_TUD_MEM_SECTION struct {
- TUD_EPBUF_DEF(buf1, CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ);
- #if CFG_TUD_AUDIO > 1
- TUD_EPBUF_DEF(buf2, CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ);
- #endif
- #if CFG_TUD_AUDIO > 2
- TUD_EPBUF_DEF(buf3, CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ);
- #endif
-} ctrl_buf;
-
-// Active alternate setting of interfaces
-tu_static uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
-
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
-tu_static uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
-#endif
-
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
-tu_static uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
-#endif
-
-// Software encoding/decoding support FIFOs
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
- #endif
- #endif
-
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
- #endif
- #endif
-
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
- #endif
- #endif
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
- #endif
- #endif
-
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
- #endif
- #endif
-
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
- tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
- tu_static tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
- #if CFG_FIFO_MUTEX
- tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
- #endif
- #endif
-#endif
+// Control buffer
+CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
// Aligned buffer for feedback EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -308,18 +209,22 @@ typedef struct
{
uint8_t rhport;
uint8_t const *p_desc;// Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+ uint8_t const *p_desc_as;// Pointer pointing to 1st Standard AS Interface Descriptor(4.9.1) - Audio Streaming descriptor defining audio function
+ uint16_t desc_length;// Length of audio function descriptor
#if CFG_TUD_AUDIO_ENABLE_EP_IN
uint8_t ep_in; // TX audio data EP.
uint16_t ep_in_sz; // Current size of TX EP
uint8_t ep_in_as_intf_num;// Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
-#endif
+ uint8_t ep_in_alt; // Current alternate setting of TX EP
+ uint16_t ep_in_fifo_threshold;// Target size for the EP IN FIFO.
+ #endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
uint8_t ep_out; // Incoming (into uC) audio data EP.
uint16_t ep_out_sz; // Current size of RX EP
uint8_t ep_out_as_intf_num;// Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
-
+ uint8_t ep_out_alt; // Current alternate setting of RX EP
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
uint8_t ep_fb;// Feedback EP.
#endif
@@ -332,8 +237,6 @@ typedef struct
bool mounted;// Device opened
- uint16_t desc_length;// Length of audio function descriptor
-
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
struct {
uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
@@ -342,7 +245,6 @@ typedef struct
uint8_t frame_shift;// bInterval-1 in unit of frame (FS), micro-frame (HS)
uint8_t compute_method;
- bool format_correction;
union {
uint8_t power_of_2;// pre-computed power of 2 shift
float float_const; // pre-computed float constant
@@ -363,82 +265,34 @@ typedef struct
} feedback;
#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-// Decoding parameters - parameters are set when alternate AS interface is set by host
-// Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- audio_format_type_t format_type_rx;
- uint8_t n_channels_rx;
-
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- audio_data_format_type_I_t format_type_I_rx;
- uint8_t n_bytes_per_sample_rx;
- uint8_t n_ff_used_rx;
- #endif
-#endif
-
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint32_t sample_rate_tx;
uint16_t packet_sz_tx[3];
uint8_t bclock_id_tx;
uint8_t interval_tx;
-#endif
-
-// Encoding parameters - parameters are set when alternate AS interface is set by host
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL)
- audio_format_type_t format_type_tx;
+ uint8_t format_type_tx;
uint8_t n_channels_tx;
uint8_t n_bytes_per_sample_tx;
-
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- audio_data_format_type_I_t format_type_I_tx;
- uint8_t n_ff_used_tx;
- #endif
#endif
/*------------- From this point, data is not cleared by bus reset -------------*/
- // Buffer for control requests
- uint8_t *ctrl_buf;
- uint8_t ctrl_buf_sz;
-
- // Current active alternate settings
- uint8_t *alt_setting;// We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
-
// EP Transfer buffers and FIFOs
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_fifo_t ep_out_ff;
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
tu_fifo_t ep_in_ff;
#endif
-// Support FIFOs for software encoding and decoding
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- tu_fifo_t *rx_supp_ff;
- uint8_t n_rx_supp_ff;
- uint16_t rx_supp_ff_sz_max;
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- uint8_t n_channels_per_ff_rx;
- #endif
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- tu_fifo_t *tx_supp_ff;
- uint8_t n_tx_supp_ff;
- uint16_t tx_supp_ff_sz_max;
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- uint8_t n_channels_per_ff_tx;
- #endif
-#endif
-
-// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR the support FIFOs are used
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
uint8_t *lin_buf_out;
#define USE_LINEAR_BUFFER_RX 1
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
uint8_t *lin_buf_in;
#define USE_LINEAR_BUFFER_TX 1
#endif
@@ -456,42 +310,30 @@ typedef struct
#define USE_LINEAR_BUFFER_RX 0
#endif
-#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_out_ff)
+#else
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_in_ff)
+#endif
//--------------------------------------------------------------------+
// WEAK FUNCTION STUBS
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
+TU_ATTR_WEAK bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
(void) rhport;
+ (void) n_bytes_sent;
(void) func_id;
(void) ep_in;
(void) cur_alt_setting;
return true;
}
-TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
- (void) rhport;
- (void) n_bytes_copied;
- (void) func_id;
- (void) ep_in;
- (void) cur_alt_setting;
- return true;
-}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
- (void) rhport;
- (void) n_bytes_received;
- (void) func_id;
- (void) ep_out;
- (void) cur_alt_setting;
- return true;
-}
-
-TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
+TU_ATTR_WEAK 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) {
(void) rhport;
(void) n_bytes_received;
(void) func_id;
@@ -502,21 +344,12 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id) {
- (void) func_id;
-}
-
TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
(void) func_id;
(void) alt_itf;
feedback_param->method = AUDIO_FEEDBACK_METHOD_DISABLED;
}
-TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
- (void) func_id;
- return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION;
-}
-
TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) {
(void) func_id;
(void) frame_number;
@@ -538,7 +371,7 @@ TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t co
}
// Invoked when audio set interface request received which closes an EP
-TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+TU_ATTR_WEAK bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
return true;
@@ -601,42 +434,29 @@ TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_reque
tu_static CFG_TUD_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received);
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received);
+static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio);
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
-static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t *audio);
+static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_sent);
#endif
static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p_request);
static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p_request);
-static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int);
-static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int);
static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id);
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
-
-#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
-static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf);
-#endif
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc);
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio);
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_size);
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_size);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq);
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt);
static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_new);
#endif
@@ -651,7 +471,7 @@ bool tud_audio_n_mounted(uint8_t func_id) {
// READ API
//--------------------------------------------------------------------+
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
uint16_t tud_audio_n_available(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
@@ -669,275 +489,108 @@ 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) {
- if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) {
+ return &_audiod_fct[func_id].ep_out_ff;
+ }
return NULL;
}
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
-// Delete all content in the support RX FIFOs
-bool tud_audio_n_clear_rx_support_ff(uint8_t func_id, uint8_t ff_idx) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
- return tu_fifo_clear(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
-}
-
-uint16_t tud_audio_n_available_support_ff(uint8_t func_id, uint8_t ff_idx) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
- return tu_fifo_count(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
-}
-
-uint16_t tud_audio_n_read_support_ff(uint8_t func_id, uint8_t ff_idx, void *buffer, uint16_t bufsize) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
- return tu_fifo_read_n(&_audiod_fct[func_id].rx_supp_ff[ff_idx], buffer, bufsize);
-}
-
-tu_fifo_t *tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx) {
- if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff) return &_audiod_fct[func_id].rx_supp_ff[ff_idx];
- return NULL;
-}
-#endif
-
-// This function is called once an audio packet is received by the USB and is responsible for putting data from USB memory into EP_OUT_FIFO (or support FIFOs + decoding of received stream into audio channels).
-// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_DECODING = 0.
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-
-static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received) {
- uint8_t idxItf = 0;
- uint8_t const *dummy2;
- uint8_t idx_audio_fct = 0;
-
- idx_audio_fct = audiod_get_audio_fct_idx(audio);
- TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2));
-
- // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO (or decoded into support RX software FIFO)
- TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
-
- #if CFG_TUD_AUDIO_ENABLE_DECODING
-
- switch (audio->format_type_rx) {
- case AUDIO_FORMAT_TYPE_UNDEFINED:
- // INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
- TU_BREAKPOINT();
- break;
-
- case AUDIO_FORMAT_TYPE_I:
-
- switch (audio->format_type_I_rx) {
- case AUDIO_DATA_FORMAT_TYPE_I_PCM:
- TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received));
- break;
-
- default:
- // DESIRED CFG_TUD_AUDIO_FORMAT_TYPE_I_RX NOT IMPLEMENTED!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_RX encoding not implemented!\r\n");
- TU_BREAKPOINT();
- break;
- }
- break;
-
- default:
- // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
- TU_BREAKPOINT();
- break;
- }
-
- // Prepare for next transmission
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
-
- #else
+static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) {
+ uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
- #if USE_LINEAR_BUFFER_RX
+ #if USE_LINEAR_BUFFER_RX
// Data currently is in linear buffer, copy into EP OUT FIFO
- TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
+ TU_VERIFY(0 < tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
// Schedule for next receive
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
- #else
+ #else
// Data is already placed in EP FIFO, schedule for next receive
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
- #endif
+ #endif
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) {
audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->ep_out_ff));
}
- #endif
-
#endif
- // Call a weak callback here - a possibility for user to get informed decoding was completed
- TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
+ // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO
+ TU_VERIFY(tud_audio_rx_done_isr(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->ep_out_alt));
return true;
}
#endif//CFG_TUD_AUDIO_ENABLE_EP_OUT
-// The following functions are used in case CFG_TUD_AUDIO_ENABLE_DECODING != 0
-#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
-
-// Decoding according to 2.3.1.5 Audio Streams
-
-// Helper function
-static inline void *audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesPerSample, void *dst, const void *dst_end, void *src, uint8_t const n_ff_used) {
- // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
- uint16_t *dst16 = dst;
- uint16_t *src16 = src;
- const uint16_t *dst_end16 = dst_end;
- uint32_t *dst32 = dst;
- uint32_t *src32 = src;
- const uint32_t *dst_end32 = dst_end;
-
- if (nBytesPerSample == 1) {
- while (dst16 < dst_end16) {
- *dst16++ = *src16++;
- src16 += n_ff_used - 1;
- }
- return src16;
- } else if (nBytesPerSample == 2) {
- while (dst32 < dst_end32) {
- *dst32++ = *src32++;
- src32 += n_ff_used - 1;
- }
- return src32;
- } else if (nBytesPerSample == 3) {
- while (dst16 < dst_end16) {
- *dst16++ = *src16++;
- *dst16++ = *src16++;
- *dst16++ = *src16++;
- src16 += 3 * (n_ff_used - 1);
- }
- return src16;
- } else// nBytesPerSample == 4
- {
- while (dst32 < dst_end32) {
- *dst32++ = *src32++;
- *dst32++ = *src32++;
- src32 += 2 * (n_ff_used - 1);
- }
- return src32;
- }
-}
-
-static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received) {
- (void) rhport;
-
- // Determine amount of samples
- uint8_t const n_ff_used = audio->n_ff_used_rx;
- uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
- uint8_t cnt_ff;
-
- // Decode
- uint8_t *src;
- uint8_t *dst_end;
-
- tu_fifo_buffer_info_t info;
-
- for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++) {
- tu_fifo_get_write_info(&audio->rx_supp_ff[cnt_ff], &info);
-
- if (info.len_lin != 0) {
- info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
- src = &audio->lin_buf_out[cnt_ff * audio->n_channels_per_ff_rx * audio->n_bytes_per_sample_rx];
- dst_end = info.ptr_lin + info.len_lin;
- src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_lin, dst_end, src, n_ff_used);
-
- // Handle wrapped part of FIFO
- info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
- if (info.len_wrap != 0) {
- dst_end = info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_wrap, dst_end, src, n_ff_used);
- }
- tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
- }
- }
-
- // Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it
- // TU_VERIFY(cnt != n_bytes);
-
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) {
- audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->rx_supp_ff[0]));
- }
- #endif
-
- return true;
-}
-#endif//CFG_TUD_AUDIO_ENABLE_DECODING
-
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
-/**
- * \brief Write data to EP in buffer
- *
- * Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
- * If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
- *
- * \param[in] func_id: Index of audio function interface
- * \param[in] data: Pointer to data array to be copied from
- * \param[in] len: # of array elements to copy
- * \return Number of bytes actually written
- */
uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
}
-bool tud_audio_n_clear_ep_in_ff(uint8_t func_id)// Delete all content in the EP IN FIFO
-{
+bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
}
tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
- if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) {
+ return &_audiod_fct[func_id].ep_in_ff;
+ }
return NULL;
}
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
-
-uint16_t tud_audio_n_flush_tx_support_ff(uint8_t func_id)// Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
-{
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- audiod_function_t *audio = &_audiod_fct[func_id];
+uint16_t tud_audio_n_get_ep_in_fifo_threshold(uint8_t func_id) {
+ if (func_id < CFG_TUD_AUDIO) return _audiod_fct[func_id].ep_in_fifo_threshold;
+ return 0;
+}
- uint16_t n_bytes_copied = tu_fifo_count(&audio->tx_supp_ff[0]);
+void tud_audio_n_set_ep_in_fifo_threshold(uint8_t func_id, uint16_t threshold) {
+ if (func_id < CFG_TUD_AUDIO && threshold < _audiod_fct[func_id].ep_in_ff.depth) {
+ _audiod_fct[func_id].ep_in_fifo_threshold = threshold;
+ }
+}
- TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio));
+static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16_t n_bytes_sent) {
+ uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
- n_bytes_copied -= tu_fifo_count(&audio->tx_supp_ff[0]);
- n_bytes_copied = n_bytes_copied * audio->tx_supp_ff[0].item_size;
+ // Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications
+ if (audio->ep_in_alt == 0) { return false; }
- return n_bytes_copied;
-}
+ // Send everything in ISO EP FIFO
+ uint16_t n_bytes_tx;
-bool tud_audio_n_clear_tx_support_ff(uint8_t func_id, uint8_t ff_idx) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
- return tu_fifo_clear(&_audiod_fct[func_id].tx_supp_ff[ff_idx]);
-}
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ // packet_sz_tx is based on total packet size, here we want size for each support buffer.
+ n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_fifo_threshold, audio->ep_in_sz);
+ #else
+ n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
+ #endif
+ #if USE_LINEAR_BUFFER_TX
+ tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+ #else
+ // Send everything in ISO EP FIFO
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+ #endif
-uint16_t tud_audio_n_write_support_ff(uint8_t func_id, uint8_t ff_idx, const void *data, uint16_t len) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
- return tu_fifo_write_n(&_audiod_fct[func_id].tx_supp_ff[ff_idx], data, len);
-}
+ // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer
+ TU_VERIFY(tud_audio_tx_done_isr(rhport, n_bytes_sent, idx_audio_fct, audio->ep_in, audio->ep_in_alt));
-tu_fifo_t *tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx) {
- if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff) return &_audiod_fct[func_id].tx_supp_ff[ff_idx];
- return NULL;
+ return true;
}
#endif
+//--------------------------------------------------------------------+
+// OTHER API
+//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_done_cb() is called in inform user
@@ -949,10 +602,15 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
// We write directly into the EP's buffer - abort if previous transfer not complete
TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int));
+ uint8_t size = tud_audio_n_version(func_id) == 2 ? sizeof(audio20_interrupt_data_t) : sizeof(audio10_interrupt_data_t);
+
+ // INT EP buffer must be large enough
+ TU_ASSERT(size <= sizeof(int_ep_buf[func_id].buf));
+
// Check length
- if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, sizeof(audio_interrupt_data_t)) == 0) {
+ if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, size) == 0) {
// Schedule transmit
- TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf)), 0);
+ TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, size), 0);
} else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int);
@@ -962,254 +620,87 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
}
#endif
-// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
-// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_ENCODING = 0 and use tud_audio_n_write.
-
-// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
-#if CFG_TUD_AUDIO_ENABLE_EP_IN
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) {
- uint8_t idxItf;
- uint8_t const *dummy2;
-
- uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
- TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, &idxItf, &dummy2));
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
+static inline bool audiod_fb_send(uint8_t func_id) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint8_t uac_version = tud_audio_n_version(func_id);
+ // Format the feedback value
+ if (uac_version == 1) {
+ uint8_t *fb = (uint8_t *) audio->fb_buf;
- // Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications
- if (audio->alt_setting[idxItf] == 0) return false;
+ // For FS format is 10.14
+ *(fb++) = (audio->feedback.value >> 2) & 0xFF;
+ *(fb++) = (audio->feedback.value >> 10) & 0xFF;
+ *(fb++) = (audio->feedback.value >> 18) & 0xFF;
+ *fb = 0;
+ } else {
+ *audio->fb_buf = audio->feedback.value;
+ }
- // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or
- // if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
- TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+ return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, uac_version == 1 ? 3 : 4);
+}
- // Send everything in ISO EP FIFO
- uint16_t n_bytes_tx;
+uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint32_t feedback;
- // If support FIFOs are used, encode and schedule transmit
- #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
- switch (audio->format_type_tx) {
- case AUDIO_FORMAT_TYPE_UNDEFINED:
- // INDIVIDUAL ENCODING PROCEDURE REQUIRED HERE!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
- TU_BREAKPOINT();
- n_bytes_tx = 0;
+ switch (audio->feedback.compute_method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
+ feedback = (cycles << audio->feedback.compute.power_of_2);
break;
- case AUDIO_FORMAT_TYPE_I:
-
- switch (audio->format_type_I_tx) {
- case AUDIO_DATA_FORMAT_TYPE_I_PCM:
-
- n_bytes_tx = audiod_encode_type_I_pcm(rhport, audio);
- break;
-
- default:
- // YOUR ENCODING IS REQUIRED HERE!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_TX encoding not implemented!\r\n");
- TU_BREAKPOINT();
- n_bytes_tx = 0;
- break;
- }
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
break;
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
+ uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
+ feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
+ } break;
+
default:
- // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
- TU_BREAKPOINT();
- n_bytes_tx = 0;
- break;
+ return 0;
}
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
-
- #else
- // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
- #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- // packet_sz_tx is based on total packet size, here we want size for each support buffer.
- n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
- #else
- n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
- #endif
- #if USE_LINEAR_BUFFER_TX
- tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
- #else
- // Send everything in ISO EP FIFO
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
- #endif
-
- #endif
-
- // Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
- TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
-
- return true;
-}
-
-#endif//CFG_TUD_AUDIO_ENABLE_EP_IN
-
-#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
-// Take samples from the support buffer and encode them into the IN EP software FIFO
-// Returns number of bytes written into linear buffer
-
-/* 2.3.1.7.1 PCM Format
-The PCM (Pulse Coded Modulation) format is the most commonly used audio format to represent audio
-data streams. The audio data is not compressed and uses a signed two’s-complement fixed point format. It
-is left-justified (the sign bit is the Msb) and data is padded with trailing zeros to fill the remaining unused
-bits of the subslot. The binary point is located to the right of the sign bit so that all values lie within the
-range [-1, +1)
- */
-
-/*
- * This function encodes channels saved within the support FIFOs into one stream by interleaving the PCM samples
- * in the support FIFOs according to 2.3.1.5 Audio Streams. It does not control justification (left or right) and
- * does not change the number of bytes per sample.
- * */
-
-// Helper function
-static inline void *audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesPerSample, void *src, const void *src_end, void *dst, uint8_t const n_ff_used) {
- // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
- uint16_t *dst16 = dst;
- uint16_t *src16 = src;
- const uint16_t *src_end16 = src_end;
- uint32_t *dst32 = dst;
- uint32_t *src32 = src;
- const uint32_t *src_end32 = src_end;
-
- if (nBytesPerSample == 1) {
- while (src16 < src_end16) {
- *dst16++ = *src16++;
- dst16 += n_ff_used - 1;
- }
- return dst16;
- } else if (nBytesPerSample == 2) {
- while (src32 < src_end32) {
- *dst32++ = *src32++;
- dst32 += n_ff_used - 1;
- }
- return dst32;
- } else if (nBytesPerSample == 3) {
- while (src16 < src_end16) {
- *dst16++ = *src16++;
- *dst16++ = *src16++;
- *dst16++ = *src16++;
- dst16 += 3 * (n_ff_used - 1);
- }
- return dst16;
- } else// nBytesPerSample == 4
- {
- while (src32 < src_end32) {
- *dst32++ = *src32++;
- *dst32++ = *src32++;
- dst32 += 2 * (n_ff_used - 1);
- }
- return dst32;
+ // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
+ // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
+ // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
+ // (audio slot = channel count samples).
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
}
-}
-
-static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t *audio) {
- // This function relies on the fact that the length of the support FIFOs was configured to be a multiple of the active sample size in bytes s.t. no sample is split within a wrap
- // This is ensured within set_interface, where the FIFOs are reconfigured according to this size
-
- // We encode directly into IN EP's linear buffer - abort if previous transfer not complete
- TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
-
- // Determine amount of samples
- uint8_t const n_ff_used = audio->n_ff_used_tx;
- uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
- uint8_t cnt_ff;
-
- for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++) {
- uint16_t const count = tu_fifo_count(&audio->tx_supp_ff[cnt_ff]);
- if (count < nBytesPerFFToSend) {
- nBytesPerFFToSend = count;
- }
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
}
- #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- const uint16_t norm_packet_sz_tx[3] = {audio->packet_sz_tx[0] / n_ff_used,
- audio->packet_sz_tx[1] / n_ff_used,
- audio->packet_sz_tx[2] / n_ff_used};
- // packet_sz_tx is based on total packet size, here we want size for each support buffer.
- nBytesPerFFToSend = audiod_tx_packet_size(norm_packet_sz_tx, nBytesPerFFToSend, audio->tx_supp_ff[0].depth, audio->ep_in_sz / n_ff_used);
- // Check if there is enough data
- if (nBytesPerFFToSend == 0) return 0;
- #else
- // Check if there is enough data
- if (nBytesPerFFToSend == 0) return 0;
- // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
- nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used);
- // Round to full number of samples (flooring)
- uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx;
- nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
- #endif
-
- // Encode
- uint8_t *dst;
- uint8_t *src_end;
-
- tu_fifo_buffer_info_t info;
-
- for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++) {
- dst = &audio->lin_buf_in[cnt_ff * audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx];
-
- tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
-
- if (info.len_lin != 0) {
- info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin);// Limit up to desired length
- src_end = (uint8_t *) info.ptr_lin + info.len_lin;
- dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_lin, src_end, dst, n_ff_used);
+ tud_audio_n_fb_set(func_id, feedback);
- // Limit up to desired length
- info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
+ return feedback;
+}
- // Handle wrapped part of FIFO
- if (info.len_wrap != 0) {
- src_end = (uint8_t *) info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_wrap, src_end, dst, n_ff_used);
- }
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
- }
- }
+ _audiod_fct[func_id].feedback.value = feedback;
- return nBytesPerFFToSend * n_ff_used;
+ return true;
}
-#endif//CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif
-// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
+uint8_t tud_audio_n_version(uint8_t func_id) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static inline bool audiod_fb_send(audiod_function_t *audio) {
- bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) && audio->feedback.format_correction;
- // Format the feedback value
- if (apply_correction) {
- uint8_t *fb = (uint8_t *) audio->fb_buf;
+ uint8_t bIntfProtocol = ((tusb_desc_interface_t const *)_audiod_fct[func_id].p_desc)->bInterfaceProtocol;
- // For FS format is 10.14
- *(fb++) = (audio->feedback.value >> 2) & 0xFF;
- *(fb++) = (audio->feedback.value >> 10) & 0xFF;
- *(fb++) = (audio->feedback.value >> 18) & 0xFF;
- *fb = 0;
+ if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V1) {
+ return 1;
+ } else if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V2) {
+ return 2;
} else {
- *audio->fb_buf = audio->feedback.value;
+ return 0; // Unknown version
}
-
- // About feedback format on FS
- //
- // 3 variables: Format | packetSize | sendSize | Working OS:
- // 16.16 4 4 Linux, Windows
- // 16.16 4 3 Linux
- // 16.16 3 4 Linux
- // 16.16 3 3 Linux
- // 10.14 4 4 Linux
- // 10.14 4 3 Linux
- // 10.14 3 4 Linux, OSX
- // 10.14 3 3 Linux, OSX
- //
- // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty ugly
- return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, apply_correction ? 3 : 4);
}
-#endif
//--------------------------------------------------------------------+
// USBD Driver API
@@ -1220,75 +711,26 @@ void audiod_init(void) {
for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
audiod_function_t *audio = &_audiod_fct[i];
- // Initialize control buffers
- switch (i) {
- case 0:
- audio->ctrl_buf = ctrl_buf.buf1;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
- break;
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
- case 1:
- audio->ctrl_buf = ctrl_buf.buf2;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
- case 2:
- audio->ctrl_buf = ctrl_buf.buf3;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
- break;
-#endif
- }
-
- // Initialize active alternate interface buffers
- switch (i) {
-#if CFG_TUD_AUDIO_FUNC_1_N_AS_INT > 0
- case 0:
- audio->alt_setting = alt_setting_1;
- break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
- case 1:
- audio->alt_setting = alt_setting_2;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
- case 2:
- audio->alt_setting = alt_setting_3;
- break;
-#endif
- }
-
// Initialize IN EP FIFO if required
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
-
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
case 0:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL);
- #endif
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
case 1:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_2), NULL);
- #endif
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
case 2:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_3), NULL);
- #endif
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Initialize linear buffers
#if USE_LINEAR_BUFFER_TX
@@ -1312,35 +754,26 @@ void audiod_init(void) {
#endif// USE_LINEAR_BUFFER_TX
// Initialize OUT EP FIFO if required
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_1));
- #endif
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
case 1:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_2));
- #endif
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
case 2:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_3));
- #endif
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Initialize linear buffers
#if USE_LINEAR_BUFFER_RX
@@ -1382,150 +815,6 @@ void audiod_init(void) {
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-
- // Initialize TX support FIFOs if required
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
-
- switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
- case 0:
- audio->tx_supp_ff = tx_supp_ff_1;
- audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO;
- audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&tx_supp_ff_1[cnt], tx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&tx_supp_ff_1[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_1[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
-
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
- case 1:
- audio->tx_supp_ff = tx_supp_ff_2;
- audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO;
- audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&tx_supp_ff_2[cnt], tx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&tx_supp_ff_2[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_2[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
-
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
- case 2:
- audio->tx_supp_ff = tx_supp_ff_3;
- audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO;
- audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&tx_supp_ff_3[cnt], tx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&tx_supp_ff_3[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_3[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
- }
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
-
- // Set encoding parameters for Type_I formats
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
- case 0:
- audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX;
- break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
- case 1:
- audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX;
- break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
- case 2:
- audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX;
- break;
- #endif
- }
-#endif// CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
-
- // Initialize RX support FIFOs if required
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
-
- switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
- case 0:
- audio->rx_supp_ff = rx_supp_ff_1;
- audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO;
- audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&rx_supp_ff_1[cnt], rx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&rx_supp_ff_1[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_1[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
-
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
- case 1:
- audio->rx_supp_ff = rx_supp_ff_2;
- audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO;
- audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&rx_supp_ff_2[cnt], rx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&rx_supp_ff_2[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_2[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
-
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
- case 2:
- audio->rx_supp_ff = rx_supp_ff_3;
- audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO;
- audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO; cnt++) {
- tu_fifo_config(&rx_supp_ff_3[cnt], rx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ, 1, true);
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&rx_supp_ff_3[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_3[cnt]), NULL);
- #endif
- }
-
- break;
- #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
- }
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
-
- // Set encoding parameters for Type_I formats
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
- case 0:
- audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX;
- break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
- case 1:
- audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX;
- break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
- case 2:
- audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX;
- break;
- #endif
- }
-#endif// CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
}
}
@@ -1540,25 +829,13 @@ void audiod_reset(uint8_t rhport) {
audiod_function_t *audio = &_audiod_fct[i];
tu_memclr(audio, ITF_MEM_RESET_SIZE);
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
tu_fifo_clear(&audio->ep_in_ff);
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_fifo_clear(&audio->ep_out_ff);
#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
- tu_fifo_clear(&audio->tx_supp_ff[cnt]);
- }
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
- tu_fifo_clear(&audio->rx_supp_ff[cnt]);
- }
-#endif
}
}
@@ -1569,7 +846,8 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
// Verify version is correct - this check can be omitted
- TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V1 ||
+ itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
// Verify interrupt control EP is enabled if demanded by descriptor
TU_ASSERT(itf_desc->bNumEndpoints <= 1);// 0 or 1 EPs are allowed
@@ -1587,21 +865,32 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
_audiod_fct[i].p_desc = (uint8_t const *) itf_desc;// Save pointer to AC descriptor which is by specification always the first one
_audiod_fct[i].rhport = rhport;
- // Setup descriptor lengths
- switch (i) {
- case 0:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
- break;
-#if CFG_TUD_AUDIO > 1
- case 1:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_2_DESC_LEN;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2
- case 2:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_3_DESC_LEN;
- break;
-#endif
+ // Calculate descriptor length
+ {
+ uint8_t const *p_desc = (uint8_t const *) itf_desc;
+ uint8_t const *p_desc_end = p_desc + max_len;
+ uint16_t total_len = sizeof(tusb_desc_interface_t);
+ // Skip Standard AC interface descriptor
+ p_desc = tu_desc_next(p_desc);
+ while (p_desc_end - p_desc > 0) {
+ // Stop if:
+ // - Non audio streaming interface descriptor found
+ // - IAD found
+ if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
+ !(((tusb_desc_interface_t const *) p_desc)->bInterfaceClass == TUSB_CLASS_AUDIO && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING))
+ || tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
+ break;
+ } else if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING) {
+ if (_audiod_fct[i].p_desc_as == NULL) {
+ _audiod_fct[i].p_desc_as = p_desc;
+ }
+ } else {
+ // nothing to do
+ }
+ total_len += p_desc[0];
+ p_desc = tu_desc_next(p_desc);
+ }
+ _audiod_fct[i].desc_length = total_len;
}
#ifdef TUP_DCD_EDPT_ISO_ALLOC
@@ -1620,52 +909,67 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
uint8_t ep_fb = 0;
#endif
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ // Unified UAC1/UAC2 endpoint processing
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Explicit feedback EP
- if (desc_ep->bmAttributes.usage == 1) {
- ep_fb = desc_ep->bEndpointAddress;
- }
- #endif
- // Data EP
- if (desc_ep->bmAttributes.usage == 0) {
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- #if CFG_TUD_AUDIO_ENABLE_EP_IN
- ep_in = desc_ep->bEndpointAddress;
- ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
- #endif
- } else {
- #if CFG_TUD_AUDIO_ENABLE_EP_OUT
- ep_out = desc_ep->bEndpointAddress;
- ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
- #endif
- }
- }
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(i) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
}
+
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Explicit feedback EP
+ if (is_feedback_ep) {
+ ep_fb = desc_ep->bEndpointAddress;
+ }
+ #else
+ (void) is_feedback_ep;
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ // Data or data with implicit feedback IN EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN && is_data_ep) {
+ ep_in = desc_ep->bEndpointAddress;
+ ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
+ }
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ // Data OUT EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT && is_data_ep) {
+ ep_out = desc_ep->bEndpointAddress;
+ ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
+ }
+ #endif
}
p_desc = tu_desc_next(p_desc);
}
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (ep_in) {
+ if (ep_in != 0) {
usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (ep_out) {
+ if (ep_out != 0) {
usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (ep_fb) {
+ if (ep_fb != 0) {
usbd_edpt_iso_alloc(rhport, ep_fb, 4);
}
#endif
@@ -1675,22 +979,27 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- if (desc_ep->bmAttributes.usage == 0) {
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- _audiod_fct[i].interval_tx = desc_ep->bInterval;
- }
+ // For data or data with implicit feedback IN EP
+ // For UAC1 this is always the case since there is no usage field
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN
+ && (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2)) {
+ _audiod_fct[i].interval_tx = desc_ep->bInterval;
}
}
- } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ } else if (tud_audio_n_version(i) == 2 &&
+ tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ // For UAC2 only, UAC1 doesn't have a clock source
if (tu_unaligned_read16(p_desc + 4) == AUDIO_TERM_TYPE_USB_STREAMING) {
_audiod_fct[i].bclock_id_tx = p_desc[8];
}
+ } else {
+ // nothing to do
}
p_desc = tu_desc_next(p_desc);
}
@@ -1700,7 +1009,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
// For each endpoint
@@ -1728,7 +1037,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
TU_ASSERT(i < CFG_TUD_AUDIO);
// This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
- uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;// - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+ uint16_t drv_len = _audiod_fct[i].desc_length;
return drv_len;
}
@@ -1737,13 +1046,25 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p
uint8_t const itf = tu_u16_low(p_request->wIndex);
// Find index of audio streaming interface
- uint8_t func_id, idxItf;
- uint8_t const *dummy;
+ uint8_t func_id;
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
- TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy));
- TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1));
+ // Default to 0 if interface not yet activated
+ uint8_t alt = 0;
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (_audiod_fct[func_id].ep_in_as_intf_num == itf) {
+ alt = _audiod_fct[func_id].ep_in_alt;
+ }
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (_audiod_fct[func_id].ep_out_as_intf_num == itf) {
+ alt = _audiod_fct[func_id].ep_out_alt;
+ }
+#endif
+
+ TU_VERIFY(tud_control_xfer(rhport, p_request, &alt, 1));
- TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]);
+ TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, alt);
return true;
}
@@ -1768,9 +1089,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
// Find index of audio streaming interface and index of interface
- uint8_t func_id, idxItf;
- uint8_t const *p_desc;
- TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc));
+ uint8_t func_id;
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
audiod_function_t *audio = &_audiod_fct[func_id];
@@ -1778,21 +1098,16 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#if CFG_TUD_AUDIO_ENABLE_EP_IN
if (audio->ep_in_as_intf_num == itf) {
audio->ep_in_as_intf_num = 0;
+ audio->ep_in_alt = 0;
#ifndef TUP_DCD_EDPT_ISO_ALLOC
usbd_edpt_close(rhport, audio->ep_in);
#endif
// Clear FIFOs, since data is no longer valid
- #if !CFG_TUD_AUDIO_ENABLE_ENCODING
tu_fifo_clear(&audio->ep_in_ff);
- #else
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
- tu_fifo_clear(&audio->tx_supp_ff[cnt]);
- }
- #endif
// Invoke callback - can be used to stop data sampling
- TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+ TU_VERIFY(tud_audio_set_itf_close_ep_cb(rhport, p_request));
audio->ep_in = 0;// Necessary?
@@ -1807,21 +1122,16 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
if (audio->ep_out_as_intf_num == itf) {
audio->ep_out_as_intf_num = 0;
+ audio->ep_out_alt = 0;
#ifndef TUP_DCD_EDPT_ISO_ALLOC
usbd_edpt_close(rhport, audio->ep_out);
#endif
// Clear FIFOs, since data is no longer valid
- #if !CFG_TUD_AUDIO_ENABLE_DECODING
tu_fifo_clear(&audio->ep_out_ff);
- #else
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
- tu_fifo_clear(&audio->rx_supp_ff[cnt]);
- }
- #endif
// Invoke callback - can be used to stop data sampling
- TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+ TU_VERIFY(tud_audio_set_itf_close_ep_cb(rhport, p_request));
audio->ep_out = 0;// Necessary?
@@ -1836,19 +1146,18 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
- // Save current alternative interface setting
- audio->alt_setting[idxItf] = alt;
-
// Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
+
+ uint8_t const *p_desc = audio->p_desc_as;
// Get pointer at end
- uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length;
// p_desc starts at required interface with alternate setting zero
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
// Find correct interface
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const *) p_desc)->bAlternateSetting == alt) {
-#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
uint8_t const *p_desc_parse_for_params = p_desc;
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
@@ -1864,61 +1173,58 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#endif
uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(func_id) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
+ }
+
//TODO: We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
usbd_edpt_clear_stall(rhport, ep_addr);
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 0x00)// Check if usage is data EP
+ // For data or data with implicit feedback IN EP
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && is_data_ep)
{
// Save address
audio->ep_in = ep_addr;
audio->ep_in_as_intf_num = itf;
+ audio->ep_in_alt = alt;
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
+ // Set the default EP IN FIFO threshold to half fifo depth.
+ audio->ep_in_fifo_threshold = audio->ep_in_ff.depth / 2;
- // If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
- #if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
-
- // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
- #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx)) * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx));
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
- tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
- }
- audio->n_ff_used_tx = audio->n_channels_tx / audio->n_channels_per_ff_tx;
- TU_ASSERT(audio->n_ff_used_tx <= audio->n_tx_supp_ff);
- #endif
+ // If flow control is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ audiod_parse_flow_control_params(audio, p_desc_parse_for_params);
+ #endif
+ // Schedule first transmit if alternate interface is not zero, as sample data is available a ZLP is loaded
+ #if USE_LINEAR_BUFFER_TX
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0));
+ #else
+ // Send everything in ISO EP FIFO
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0));
#endif
-
- // Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
- // It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
- TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT)// Checking usage not necessary
- {
+ // Checking usage not necessary
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT && is_data_ep) {
// Save address
audio->ep_out = ep_addr;
audio->ep_out_as_intf_num = itf;
+ audio->ep_out_alt = alt;
audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
- #if CFG_TUD_AUDIO_ENABLE_DECODING
- audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
-
- // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sample_rx) * audio->n_bytes_per_sample_rx;
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
- tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
- }
- audio->n_ff_used_rx = audio->n_channels_rx / audio->n_channels_per_ff_rx;
- TU_ASSERT(audio->n_ff_used_rx <= audio->n_rx_supp_ff);
- #endif
- #endif
-
// Prepare for incoming data
#if USE_LINEAR_BUFFER_RX
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
@@ -1928,12 +1234,18 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1)// Check if usage is explicit data feedback
- {
+ // Check if usage is explicit data feedback
+ if (is_feedback_ep) {
audio->ep_fb = ep_addr;
audio->feedback.frame_shift = desc_ep->bInterval - 1;
+ // Schedule first feedback transmit
+ audiod_fb_send(func_id);
}
+ #else
+ (void) is_feedback_ep;
#endif
+#else
+ (void) is_feedback_ep;
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
foundEPs += 1;
@@ -1947,55 +1259,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Prepare feedback computation if endpoint is available
- if (audio->ep_fb != 0) {
- audio_feedback_params_t fb_param;
-
- tud_audio_feedback_params_cb(func_id, alt, &fb_param);
- audio->feedback.compute_method = fb_param.method;
-
- if (TUSB_SPEED_FULL == tud_speed_get())
- audio->feedback.format_correction = tud_audio_feedback_format_correction_cb(func_id);
-
- // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
- uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
- audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
- audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
-
- switch (fb_param.method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
- // Initialize the threshold level to half filled
- uint16_t fifo_lvl_thr;
- #if CFG_TUD_AUDIO_ENABLE_DECODING
- fifo_lvl_thr = tu_fifo_depth(&audio->rx_supp_ff[0]) / 2;
- #else
- fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
- #endif
- audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
- audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_lvl_thr) << 16;
- // Avoid 64bit division
- uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
- audio->feedback.compute.fifo_count.nom_value = nominal;
- audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_lvl_thr);
- audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_lvl_thr);
- // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
- if (tud_speed_get() == TUSB_SPEED_HIGH) {
- audio->feedback.compute.fifo_count.rate_const[0] /= 8;
- audio->feedback.compute.fifo_count.rate_const[1] /= 8;
- }
- } break;
-
- // nothing to do
- default:
- break;
- }
- }
+ // Prepare feedback computation parameters
+ TU_VERIFY(audiod_fb_params_prepare(func_id, alt));
#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// We are done - abort loop
@@ -2047,20 +1312,23 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf);
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
#endif
// Invoke callback
- return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_entity_cb(rhport, p_request, ctrl_buf);
} else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
- return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_itf_cb(rhport, p_request, ctrl_buf);
}
} break;
@@ -2070,8 +1338,32 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
- // Invoke callback
- return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ if (tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_in == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf) & 0x00FFFFFF;
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
+ }
+ }
+#endif
+
+ // Invoke callback
+ bool ret = tud_audio_set_req_ep_cb(rhport, p_request, ctrl_buf);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (ret && tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_out == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ audiod_fb_params_prepare(func_id, _audiod_fct[func_id].ep_out_alt);
+ }
+ }
+ }
+#endif
+ return ret;
} break;
// Unknown/Unsupported recipient
default:
@@ -2156,7 +1448,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
- TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, ctrl_buf, sizeof(ctrl_buf)));
return true;
}
@@ -2170,6 +1462,8 @@ bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
return audiod_control_request(rhport, request);
} else if (stage == CONTROL_STAGE_DATA) {
return audiod_control_complete(rhport, request);
+ } else {
+ // nothing to do
}
return true;
@@ -2179,12 +1473,11 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
(void) result;
(void) xferred_bytes;
+ #if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// Search for interface belonging to given end point address and proceed as required
for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) {
audiod_function_t *audio = &_audiod_fct[func_id];
-#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
-
// Data transmission of control interrupt finished
if (audio->ep_int == ep_addr) {
// According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
@@ -2198,12 +1491,28 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
return true;
}
-#endif
+ }
+ #else
+ (void) rhport;
+ (void) ep_addr;
+ #endif
+
+ return false;
+}
+
+bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) result;
+ (void) xferred_bytes;
+
+ // Search for interface belonging to given end point address and proceed as required
+ for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
+ {
+ audiod_function_t* audio = &_audiod_fct[func_id];
#if CFG_TUD_AUDIO_ENABLE_EP_IN
// Data transmission of audio packet finished
- if (audio->ep_in == ep_addr && audio->alt_setting != 0) {
+ if (audio->ep_in == ep_addr) {
// USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
// That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
// This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
@@ -2213,32 +1522,24 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// This is the only place where we can fill something into the EPs buffer!
// Load new data
- TU_VERIFY(audiod_tx_done_cb(rhport, audio));
-
- // Transmission of ZLP is done by audiod_tx_done_cb()
+ audiod_tx_xfer_isr(rhport, audio, (uint16_t) xferred_bytes);
return true;
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-
// New audio packet received
if (audio->ep_out == ep_addr) {
- TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes));
+ audiod_rx_xfer_isr(rhport, audio, (uint16_t) xferred_bytes);
return true;
}
-
-
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Transmission of feedback EP finished
if (audio->ep_fb == ep_addr) {
- tud_audio_fb_done_cb(func_id);
-
// Schedule a transmit with the new value if EP is not busy
- if (usbd_edpt_claim(rhport, audio->ep_fb)) {
- // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
- return audiod_fb_send(audio);
- }
+ // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
+ audiod_fb_send(func_id);
+ return true;
}
#endif
#endif
@@ -2249,30 +1550,72 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq) {
- // Check if frame interval is within sane limits
- // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
- // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
- // this lower limit ensures the measures feedback value has sufficient precision
- uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
- uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+ // Prepare feedback computation if endpoint is available
+ if (audio->ep_fb != 0) {
+ audio_feedback_params_t fb_param = {0};
- if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
- TU_LOG1(" UAC2 feedback interval too small\r\n");
- TU_BREAKPOINT();
- return false;
- }
+ tud_audio_feedback_params_cb(func_id, alt, &fb_param);
+ audio->feedback.compute_method = fb_param.method;
- // Check if parameters really allow for a power of two division
- if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) {
- audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
- audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq));
- } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
- audio->feedback.compute.float_const = (float) sample_freq / (float) mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
- } else {
- audio->feedback.compute.fixed.sample_freq = sample_freq;
- audio->feedback.compute.fixed.mclk_freq = mclk_freq;
+ // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
+ uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
+ audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
+ audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
+
+ switch (fb_param.method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: {
+ // Check if frame interval is within sane limits
+ // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+
+ // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
+ // this lower limit ensures the measures feedback value has sufficient precision
+ uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
+ uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+
+ if ((((1UL << k) * fb_param.sample_freq / fb_param.frequency.mclk_freq) + 1) > n_frame) {
+ TU_LOG1(" UAC2 feedback interval too small\r\n");
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ // Check if parameters really allow for a power of two division
+ if ((fb_param.frequency.mclk_freq % fb_param.sample_freq) == 0 && tu_is_power_of_two(fb_param.frequency.mclk_freq / fb_param.sample_freq)) {
+ audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
+ audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(fb_param.frequency.mclk_freq / fb_param.sample_freq));
+ } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
+ audio->feedback.compute.float_const = (float) fb_param.sample_freq / (float) fb_param.frequency.mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
+ } else {
+ audio->feedback.compute.fixed.sample_freq = fb_param.sample_freq;
+ audio->feedback.compute.fixed.mclk_freq = fb_param.frequency.mclk_freq;
+ }
+ } break;
+
+ case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
+ // Determine FIFO threshold
+ uint16_t fifo_threshold = fb_param.fifo_count.fifo_threshold ? fb_param.fifo_count.fifo_threshold : tu_fifo_depth(&audio->ep_out_ff) / 2;
+ audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_threshold;
+ audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_threshold) << 16;
+ // Avoid 64bit division
+ uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
+ audio->feedback.compute.fifo_count.nom_value = nominal;
+ audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_threshold);
+ audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_threshold);
+ // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ audio->feedback.compute.fifo_count.rate_const[0] /= 8;
+ audio->feedback.compute.fifo_count.rate_const[1] /= 8;
+ }
+ } break;
+
+ // nothing to do
+ default:
+ break;
+ }
}
return true;
@@ -2296,62 +1639,15 @@ static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_n
feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1];
}
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
- audio->feedback.value = feedback;
-
- // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
- if (usbd_edpt_claim(audio->rhport, audio->ep_fb)) {
- audiod_fb_send(audio);
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
}
-}
-
-uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
- audiod_function_t *audio = &_audiod_fct[func_id];
- uint32_t feedback;
-
- switch (audio->feedback.compute_method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- feedback = (cycles << audio->feedback.compute.power_of_2);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
- uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
- feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
- } break;
-
- default:
- return 0;
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
}
-
- // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
- // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
- // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
- // (audio slot = channel count samples).
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
-
- tud_audio_n_fb_set(func_id, feedback);
-
- return feedback;
+ audio->feedback.value = feedback;
}
-bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
-
- _audiod_fct[func_id].feedback.value = feedback;
-
- // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
- if (usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb)) {
- return audiod_fb_send(&_audiod_fct[func_id]);
- }
-
- return true;
-}
#endif
TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
@@ -2419,71 +1715,27 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
}
// Crop length
- if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
+ if (len > sizeof(ctrl_buf)) len = sizeof(ctrl_buf);
// Copy into buffer
- TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz, data, (size_t) len));
+ TU_VERIFY(0 == tu_memcpy_s(ctrl_buf, sizeof(ctrl_buf), data, (size_t) len));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- // Find data for sampling_frequency_control
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
- uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ // Find data for sampling_frequency_control
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(ctrl_buf);
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
}
#endif
// Schedule transmit
- return tud_control_xfer(rhport, p_request, (void *) _audiod_fct[func_id].ctrl_buf, len);
-}
-
-// This helper function finds for a given audio function and AS interface number the index of the attached driver structure, the index of the interface in the audio function
-// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
-// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
-static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int) {
- if (audio->p_desc) {
- // Get pointer at end
- uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
-
- // Advance past AC descriptors
- uint8_t const *p_desc = tu_desc_next(audio->p_desc);
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
-
- uint8_t tmp = 0;
- // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
- while (p_desc_end - p_desc > 0) {
- // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bAlternateSetting == 0) {
- if (((tusb_desc_interface_t const *) p_desc)->bInterfaceNumber == itf) {
- *idxItf = tmp;
- *pp_desc_int = p_desc;
- return true;
- }
- // Increase index, bytes read, and pointer
- tmp++;
- }
- p_desc = tu_desc_next(p_desc);
- }
- }
- return false;
-}
-
-// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function
-// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
-// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
-static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int) {
- // Loop over audio driver interfaces
- uint8_t i;
- for (i = 0; i < CFG_TUD_AUDIO; i++) {
- if (audiod_get_AS_interface_index(itf, &_audiod_fct[i], idxItf, pp_desc_int)) {
- *func_id = i;
- return true;
- }
- }
-
- return false;
+ return tud_control_xfer(rhport, p_request, ctrl_buf, len);
}
// Verify an entity with the given ID exists and returns also the corresponding driver index
@@ -2494,13 +1746,11 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) {
// Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);// Points to CS AC descriptor
- uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength + p_desc;
p_desc = tu_desc_next(p_desc);// Get past CS AC descriptor
- // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
- while (p_desc_end - p_desc > 0) {
- if (p_desc[3] == entityID)// Entity IDs are always at offset 3
- {
+ while (_audiod_fct[i].p_desc_as - p_desc > 0) {
+ // Entity IDs are always at offset 3
+ if (p_desc[3] == entityID) {
*func_id = i;
return true;
}
@@ -2514,13 +1764,13 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) {
uint8_t i;
for (i = 0; i < CFG_TUD_AUDIO; i++) {
- if (_audiod_fct[i].p_desc) {
+ if (_audiod_fct[i].p_desc != NULL) {
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == itf) {
*func_id = i;
return true;
}
@@ -2539,8 +1789,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) {
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Advance past AC descriptors - EP we look for are streaming EPs
- uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
+ uint8_t const *p_desc = _audiod_fct[i].p_desc_as;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
@@ -2555,89 +1804,39 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) {
return false;
}
-#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
-// p_desc points to the AS interface of alternate setting zero
-// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
-// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
-static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf) {
- #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) return;// Abort, this interface has no EP, this driver does not support this currently
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num) return;
- #endif
- #if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_out_as_intf_num) return;
- #endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc) {
p_desc = tu_desc_next(p_desc);// Exclude standard AS interface descriptor of current alternate interface descriptor
- // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
- while (p_desc_end - p_desc > 0) {
- // Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
- // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) {
- #if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (as_itf == audio->ep_in_as_intf_num) {
- audio->n_channels_tx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
- audio->format_type_tx = (audio_format_type_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType);
-
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- audio->format_type_I_tx = (audio_data_format_type_I_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bmFormats);
- #endif
- }
- #endif
-
- #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- if (as_itf == audio->ep_out_as_intf_num) {
- audio->n_channels_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
- audio->format_type_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType;
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- audio->format_type_I_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bmFormats;
- #endif
+ if (tud_audio_n_version(audiod_get_audio_fct_idx(audio)) == 1) {
+ p_desc = tu_desc_next(p_desc);// Exclude Class-Specific AS Interface Descriptor(4.5.2) to get to format type descriptor
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE) {
+ audio->format_type_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bFormatType;
+ if (audio->format_type_tx == AUDIO10_FORMAT_TYPE_I) {
+ audio->n_channels_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bNrChannels;
+ audio->n_bytes_per_sample_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bSubFrameSize;
+ // Save sample rate - needed when EP doesn't support setting sample rate
+ audio->sample_rate_tx = tu_unaligned_read32(((audio10_desc_type_I_format_n_t(1) const *) p_desc)->tSamFreq) & 0x00FFFFFF;
}
- #endif
}
-
- // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
- #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I) {
- #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break;// Abort loop, this interface has no EP, this driver does not support this currently
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num) break;
- #endif
- #if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_out_as_intf_num) break;
- #endif
-
- #if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (as_itf == audio->ep_in_as_intf_num) {
- audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
- }
- #endif
-
- #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- if (as_itf == audio->ep_out_as_intf_num) {
- audio->n_bytes_per_sample_rx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
+ } else {
+ // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_AS_GENERAL) {
+ audio->n_channels_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
+ audio->format_type_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bFormatType;
+ // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+ p_desc = tu_desc_next(p_desc);
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE && ((audio20_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO20_FORMAT_TYPE_I) {
+ audio->n_bytes_per_sample_tx = ((audio20_desc_type_I_format_t const *) p_desc)->bSubslotSize;
}
- #endif
}
- #endif
-
- // Other format types are not supported yet
-
- p_desc = tu_desc_next(p_desc);
}
}
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
- TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
+ // AUDIO20_FORMAT_TYPE_I = AUDIO10_FORMAT_TYPE_I
+ TU_VERIFY(audio->format_type_tx == AUDIO20_FORMAT_TYPE_I);
TU_VERIFY(audio->n_channels_tx);
TU_VERIFY(audio->n_bytes_per_sample_tx);
TU_VERIFY(audio->interval_tx);
@@ -2672,22 +1871,22 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
return true;
}
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_depth) {
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_depth) {
// Flow control need a FIFO size of at least 4*Navg
- if (norminal_size[1] && norminal_size[1] <= fifo_depth * 4) {
+ if (nominal_size[1] && nominal_size[1] <= fifo_depth * 4) {
// Use blackout to prioritize normal size packet
static int ctrl_blackout = 0;
uint16_t packet_size;
- uint16_t slot_size = norminal_size[2] - norminal_size[1];
- if (data_count < norminal_size[0]) {
+ uint16_t slot_size = nominal_size[2] - nominal_size[1];
+ if (data_count < nominal_size[0]) {
// If you get here frequently, then your I2S clock deviation is too big !
packet_size = 0;
- } else if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout) {
- packet_size = norminal_size[0];
+ } else if (data_count < (fifo_threshold - slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[0];
ctrl_blackout = 10;
- } else if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout) {
- packet_size = norminal_size[2];
- if (norminal_size[0] == norminal_size[1]) {
+ } else if (data_count > (fifo_threshold + slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[2];
+ if (nominal_size[0] == nominal_size[1]) {
// nav > INT(nav), eg. 44.1k, 88.2k
ctrl_blackout = 0;
} else {
@@ -2695,7 +1894,7 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
ctrl_blackout = 10;
}
} else {
- packet_size = norminal_size[1];
+ packet_size = nominal_size[1];
if (ctrl_blackout) {
ctrl_blackout--;
}
@@ -2710,11 +1909,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
#endif
// No security checks here - internal function only which should always succeed
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO; cnt++) {
- if (&_audiod_fct[cnt] == audio) return cnt;
- }
- return 0;
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
+ return (uint8_t) (audio - _audiod_fct);
}
#endif // (CFG_TUD_ENABLED && CFG_TUD_AUDIO)
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
index 0a7bff212..1e0c46915 100644
--- a/src/class/audio/audio_device.h
+++ b/src/class/audio/audio_device.h
@@ -26,8 +26,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_AUDIO_DEVICE_H_
-#define _TUSB_AUDIO_DEVICE_H_
+#ifndef TUSB_AUDIO_DEVICE_H_
+#define TUSB_AUDIO_DEVICE_H_
#include "audio.h"
@@ -37,50 +37,10 @@
// All sizes are in bytes!
-#ifndef CFG_TUD_AUDIO_FUNC_1_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_DESC_LEN
-#error You must tell the driver the length of the audio function descriptor including IAD descriptor
-#endif
-#endif
-
-// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces
-#ifndef CFG_TUD_AUDIO_FUNC_1_N_AS_INT
-#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
-#endif
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_N_AS_INT
-#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_N_AS_INT
-#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
-#endif
-#endif
-
-// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
-#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
-
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
-#endif
-
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ
-#error You must define an audio class control request buffer size!
-#endif
+// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your
+// biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
+#ifndef CFG_TUD_AUDIO_CTRL_BUF_SZ
+#define CFG_TUD_AUDIO_CTRL_BUF_SZ 64
#endif
// End point sizes IN BYTES - Limits: Full Speed <= 1023, High Speed <= 1024
@@ -182,7 +142,8 @@
#endif
#endif
-// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
+// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a
+// virtual device where data is perfectly synchronized to USB clock.
#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
#endif
@@ -192,12 +153,6 @@
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
#endif
-// Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set().
-// Can be override by tud_audio_feedback_format_correction_cb()
-#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
-#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
-#endif
-
// Enable/disable interrupt EP (required for notifying host of control changes)
#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
@@ -206,153 +161,6 @@
// Audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
#define CFG_TUD_AUDIO_INTERRUPT_EP_SZ 6
-// Use software encoding/decoding
-
-// The software coding feature of the driver is not mandatory. It is useful if, for instance, you have two I2S streams which need to be interleaved
-// into a single PCM stream as SAMPLE_1 | SAMPLE_2 | SAMPLE_3 | SAMPLE_4.
-//
-// Currently, only PCM type I encoding/decoding is supported!
-//
-// If the coding feature is to be used, support FIFOs need to be configured. Their sizes and numbers are defined below.
-
-// Encoding/decoding is done in software and thus time consuming. If you can encode/decode your stream more efficiently do not use the
-// support FIFOs but write/read directly into/from the EP_X_SW_BUFFER_FIFOs using
-// - tud_audio_n_write() or
-// - tud_audio_n_read().
-// To write/read to/from the support FIFOs use
-// - tud_audio_n_write_support_ff() or
-// - tud_audio_n_read_support_ff().
-//
-// The encoding/decoding format type done is defined below.
-//
-// The encoding/decoding starts when the private callback functions
-// - audio_tx_done_cb()
-// - audio_rx_done_cb()
-// are invoked. If support FIFOs are used, the corresponding encoding/decoding functions are called from there.
-// Once encoding/decoding is done the result is put directly into the EP_X_SW_BUFFER_FIFOs. You can use the public callback functions
-// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
-// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
-// if you want to get informed what happened.
-//
-// If you don't use the support FIFOs you may use the public callback functions
-// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
-// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
-// to write/read from/into the EP_X_SW_BUFFER_FIFOs at the right time.
-//
-// If you need a different encoding which is not support so far implement it in the
-// - audio_tx_done_cb()
-// - audio_rx_done_cb()
-// functions.
-
-// Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size
-// The actual coding parameters of active AS alternate interface is parsed from the descriptors
-
-// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple
-// of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sample_sz) * sample_sz)!
-// This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!!
-
-// For PCM encoding/decoding
-
-#ifndef CFG_TUD_AUDIO_ENABLE_ENCODING
-#define CFG_TUD_AUDIO_ENABLE_ENCODING 0
-#endif
-
-#ifndef CFG_TUD_AUDIO_ENABLE_DECODING
-#define CFG_TUD_AUDIO_ENABLE_DECODING 0
-#endif
-
-// This enabling allows to save the current coding parameters e.g. # of bytes per sample etc. - TYPE_I includes common PCM encoding
-#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
-#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 0
-#endif
-
-#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
-#define CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING 0
-#endif
-
-// Type I Coding parameters not given within UAC2 descriptors
-// It would be possible to allow for a more flexible setting and not fix this parameter as done below. However, this is most often not needed and kept for later if really necessary. The more flexible setting could be implemented within set_interface(), however, how the values are saved per alternate setting is to be determined!
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
-#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#endif
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
-#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#if CFG_TUD_AUDIO > 1
-#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2
-#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX
-#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
-#endif
-#endif
-#endif
-
-// Remaining types not support so far
-
-// Number of support FIFOs to set up - multiple channels can be handled by one FIFO - very common is two channels per FIFO stemming from one I2S interface
-#ifndef CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO 0
-#endif
-
-#ifndef CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO
-#define CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO 0
-#endif
-
-// Size of support FIFOs IN BYTES - if size > 0 there are as many FIFOs set up as CFG_TUD_AUDIO_FUNC_X_N_TX_SUPP_SW_FIFO and CFG_TUD_AUDIO_FUNC_X_N_RX_SUPP_SW_FIFO
-#ifndef CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of TX channels) / (# of TX support FIFOs) * max(# of bytes per sample)
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ 0
-#endif
-
-#ifndef CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of RX channels) / (# of RX support FIFOs) * max(# of bytes per sample)
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ 0
-#endif
-#ifndef CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ
-#define CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ 0
-#endif
-
-//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
-
-// Supported types of this driver:
-// AUDIO_DATA_FORMAT_TYPE_I_PCM - Required definitions: CFG_TUD_AUDIO_N_CHANNELS and CFG_TUD_AUDIO_BYTES_PER_CHANNEL
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -366,75 +174,45 @@ extern "C" {
// Application API (Multiple Interfaces)
// CFG_TUD_AUDIO > 1
//--------------------------------------------------------------------+
-bool tud_audio_n_mounted (uint8_t func_id);
+bool tud_audio_n_mounted(uint8_t func_id);
+uint8_t tud_audio_n_version(uint8_t func_id);
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
-uint16_t tud_audio_n_available (uint8_t func_id);
-uint16_t tud_audio_n_read (uint8_t func_id, void* buffer, uint16_t bufsize);
-bool tud_audio_n_clear_ep_out_ff (uint8_t func_id); // Delete all content in the EP OUT FIFO
-tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
-bool tud_audio_n_clear_rx_support_ff (uint8_t func_id, uint8_t ff_idx); // Delete all content in the support RX FIFOs
-uint16_t tud_audio_n_available_support_ff (uint8_t func_id, uint8_t ff_idx);
-uint16_t tud_audio_n_read_support_ff (uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize);
-tu_fifo_t* tud_audio_n_get_rx_support_ff (uint8_t func_id, uint8_t ff_idx);
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
-uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
-bool tud_audio_n_clear_ep_in_ff (uint8_t func_id); // Delete all content in the EP IN FIFO
-tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
+#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 && CFG_TUD_AUDIO_ENABLE_ENCODING
-uint16_t tud_audio_n_flush_tx_support_ff (uint8_t func_id); // Force all content in the support TX FIFOs to be written into EP SW FIFO
-bool tud_audio_n_clear_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
-uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len);
-tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
+#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);
+uint16_t tud_audio_n_get_ep_in_fifo_threshold(uint8_t func_id);
+void tud_audio_n_set_ep_in_fifo_threshold(uint8_t func_id, uint16_t threshold);
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
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);
+static inline uint8_t tud_audio_version (void);
-// RX API
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
-static inline uint16_t tud_audio_available (void);
-static inline bool tud_audio_clear_ep_out_ff (void); // Delete all content in the EP OUT FIFO
-static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
-static inline tu_fifo_t* tud_audio_get_ep_out_ff (void);
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
-static inline bool tud_audio_clear_rx_support_ff (uint8_t ff_idx);
-static inline uint16_t tud_audio_available_support_ff (uint8_t ff_idx);
-static inline uint16_t tud_audio_read_support_ff (uint8_t ff_idx, void* buffer, uint16_t bufsize);
-static inline tu_fifo_t* tud_audio_get_rx_support_ff (uint8_t ff_idx);
-#endif
-
-// TX API
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
-static inline uint16_t tud_audio_write (const void * data, uint16_t len);
-static inline bool tud_audio_clear_ep_in_ff (void);
-static inline tu_fifo_t* tud_audio_get_ep_in_ff (void);
+#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 && CFG_TUD_AUDIO_ENABLE_ENCODING
-static inline uint16_t tud_audio_flush_tx_support_ff (void);
-static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t ff_idx);
-static inline uint16_t tud_audio_write_support_ff (uint8_t ff_idx, const void * data, uint16_t len);
-static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
+#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
@@ -444,71 +222,91 @@ static inline bool tud_audio_int_write (const audio_interru
#endif
// Buffer control EP data and schedule a transmit
-// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a
-// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
-// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such
-// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time.
+// This function is intended to be used if you do not have a persistent buffer or memory location available
+// (e.g. non-local variables) and need to answer onto a get request. This function buffers your answer request
+// frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
+// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer
+// pointer in pointing. If you already have such available you may directly use 'tud_control_xfer(...)'. In this
+// case data does not need to be copied into an additional buffer and you save some time.
// If the request's wLength is zero, a status packet is sent instead.
-bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request,
+ void* data, uint16_t len);
//--------------------------------------------------------------------+
// Application Callback API
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
-bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+// 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
-bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
-bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+// 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
-void tud_audio_fb_done_cb(uint8_t func_id);
-
// 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.
+// Advantage: No SOF interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus
+// less CPU load, well tested (Windows, Linux, OSX) with a reliable result so far.
+// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is
+// introduced.
//
// Option 2 - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED / AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
-// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from
-// which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
+// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information
+// about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle
+// count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
// See tud_audio_set_fb_params() and tud_audio_feedback_update()
-// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a smaller delay is possible.
-// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point is the reading of the cycle counter value of f_m.
-// It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
-// Long-term drift could occur since error is accumulated.
+// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a
+// smaller delay is possible.
+// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point
+// is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -,
+// hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is
+// introduced). Long-term drift will cause the FIFO under/overflow, you still needs to correct it somehow.
//
// Option 3 - manual
-// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer.
-// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load.
-// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay is introduced.
+// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined
+// e.g. from some fill status of some FIFO buffer.
+// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus
+// less CPU load.
+// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay
+// is introduced.
-// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed.
+// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at
+// FB endpoint interval till it's changed.
//
-// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default,
-// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set or tud_audio_feedback_format_correction_cb()
-// return true, then tinyusb expects 16.16 format and handles the conversion to 10.14 on FS.
+// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus
+// Specification Revision 2.0 5.12.4.2). For simplicity, this function always uses 16.16 format. For FS devices,
+// the driver will automatically convert the value to 10.14 format.
//
-// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and it seems the
-// driver can work with either format.
+// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0
+// devices. On Linux and it seems the driver can work with either format.
//
-// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value.
+// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the
+// feature is used, the user can not set the feedback value.
//
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
-// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13
-// for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
-// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K)
-// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
+// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames
+// need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency
+// e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is
+// available and locked to f_s)
+// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where
+// P = min( ceil(log2(f_m / f_s)), K)
+// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles
+// within fb_n_frames
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
// Update feedback value with passed MCLK cycles since last time this update function is called.
@@ -535,10 +333,12 @@ typedef struct {
union {
struct {
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
- }frequency;
-
+ } frequency;
+ struct {
+ uint16_t fifo_threshold; // Target FIFO threshold level, default to half FIFO if not set
+ } fifo_count;
};
-}audio_feedback_params_t;
+} audio_feedback_params_t;
// Invoked when needed to set feedback parameters
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
@@ -548,13 +348,10 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba
// frame_number : current SOF count
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
-
-// (Full-Speed only) Callback to set feedback format correction is applied or not,
-// default to CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if not implemented.
-bool tud_audio_feedback_format_correction_cb(uint8_t func_id);
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+// Invoked when an interrupt notification transfer is complete
void tud_audio_int_done_cb(uint8_t rhport);
#endif
@@ -562,7 +359,10 @@ void tud_audio_int_done_cb(uint8_t rhport);
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);
+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);
@@ -586,120 +386,70 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
// Inline Functions
//--------------------------------------------------------------------+
-static inline bool tud_audio_mounted(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_mounted(void) {
return tud_audio_n_mounted(0);
}
-// RX API
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_audio_version(void) {
+ return tud_audio_n_version(0);
+}
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-static inline uint16_t tud_audio_available(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_available(void) {
return tud_audio_n_available(0);
}
-static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize) {
return tud_audio_n_read(0, buffer, bufsize);
}
-static inline bool tud_audio_clear_ep_out_ff(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_out_ff(void) {
return tud_audio_n_clear_ep_out_ff(0);
}
-static inline tu_fifo_t* tud_audio_get_ep_out_ff(void)
-{
+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_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
-
-static inline bool tud_audio_clear_rx_support_ff(uint8_t ff_idx)
-{
- return tud_audio_n_clear_rx_support_ff(0, ff_idx);
-}
-
-static inline uint16_t tud_audio_available_support_ff(uint8_t ff_idx)
-{
- return tud_audio_n_available_support_ff(0, ff_idx);
-}
-
-static inline uint16_t tud_audio_read_support_ff(uint8_t ff_idx, void* buffer, uint16_t bufsize)
-{
- return tud_audio_n_read_support_ff(0, ff_idx, buffer, bufsize);
-}
-
-static inline tu_fifo_t* tud_audio_get_rx_support_ff(uint8_t ff_idx)
-{
- return tud_audio_n_get_rx_support_ff(0, ff_idx);
-}
-
-#endif
-
-// TX API
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
-static inline uint16_t tud_audio_write(const void * data, uint16_t len)
-{
+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);
}
-static inline bool tud_audio_clear_ep_in_ff(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_in_ff(void) {
return tud_audio_n_clear_ep_in_ff(0);
}
-static inline tu_fifo_t* tud_audio_get_ep_in_ff(void)
-{
+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_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
-
-static inline uint16_t tud_audio_flush_tx_support_ff(void)
-{
- return tud_audio_n_flush_tx_support_ff(0);
-}
-
-static inline uint16_t tud_audio_clear_tx_support_ff(uint8_t ff_idx)
-{
- return tud_audio_n_clear_tx_support_ff(0, ff_idx);
-}
-
-static inline uint16_t tud_audio_write_support_ff(uint8_t ff_idx, const void * data, uint16_t len)
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_get_ep_in_fifo_threshold(void)
{
- return tud_audio_n_write_support_ff(0, ff_idx, data, len);
+ return tud_audio_n_get_ep_in_fifo_threshold(0);
}
-static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
+TU_ATTR_ALWAYS_INLINE static inline void tud_audio_set_ep_in_fifo_threshold(uint16_t threshold)
{
- return tud_audio_n_get_tx_support_ff(0, ff_idx);
+ tud_audio_n_set_ep_in_fifo_threshold(0, threshold);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
-static inline bool tud_audio_int_write(const audio_interrupt_data_t * data)
-{
+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
-
-static inline bool tud_audio_fb_set(uint32_t feedback)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_fb_set(uint32_t feedback) {
return tud_audio_n_fb_set(0, feedback);
}
-
#endif
//--------------------------------------------------------------------+
@@ -711,13 +461,14 @@ 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_ */
+#endif /* TUSB_AUDIO_DEVICE_H_ */
/** @} */
/** @} */
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
index 45cbf2d98..3f1529cb6 100755
--- a/src/class/bth/bth_device.c
+++ b/src/class/bth/bth_device.c
@@ -43,7 +43,7 @@ typedef struct {
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[2];// Not used yet
uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
// Previous amount of bytes sent when issuing ZLP
@@ -61,8 +61,7 @@ typedef struct {
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)
-{
+static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) {
uint8_t const rhport = 0;
// skip if previous transfer not complete
@@ -74,6 +73,27 @@ static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
}
//--------------------------------------------------------------------+
+// 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
//--------------------------------------------------------------------+
@@ -82,13 +102,11 @@ static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
// WRITE API
//--------------------------------------------------------------------+
-bool tud_bt_event_send(void *event, uint16_t event_len)
-{
+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)
-{
+bool tud_bt_acl_data_send(void *event, uint16_t event_len) {
return bt_tx_data(_btd_itf.ep_acl_in, event, event_len);
}
@@ -103,13 +121,11 @@ bool btd_deinit(void) {
return true;
}
-void btd_reset(uint8_t rhport)
-{
- (void)rhport;
+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)
-{
+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
@@ -118,8 +134,9 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
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);
+ 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);
@@ -131,10 +148,10 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
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);
+ 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,
+ 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);
@@ -146,10 +163,10 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
_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);
+ 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));
+ 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);
@@ -158,13 +175,14 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
// 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);
+ 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);
+ 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);
@@ -172,7 +190,7 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
// 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);
+ 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;
@@ -182,29 +200,30 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
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);
+ 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)
- {
+ 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);
+ 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[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);
+ 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[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;
}
@@ -215,44 +234,32 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_
// 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;
+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 (stage == CONTROL_STAGE_SETUP) {
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
- request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
- {
+ 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)
- {
+ } 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
- {
+ } 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;
+ } else
+ return false;
return tud_control_xfer(rhport, request, &_btd_epbuf.hci_cmd, sizeof(bt_hci_cmd_t));
- }
- else if ( stage == CONTROL_STAGE_DATA )
- {
+ } else if (stage == CONTROL_STAGE_DATA) {
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- if (tud_bt_hci_cmd_cb) {
- tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t)));
- }
+ tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t)));
}
return true;
@@ -261,19 +268,14 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
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)
- {
- if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes);
+ 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)
- {
- if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes);
- }
- else if (ep_addr == _btd_itf.ep_acl_in)
- {
+ } 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
@@ -281,12 +283,12 @@ bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
// Send zero-length packet
tud_bt_acl_data_send(NULL, 0);
- } else if (tud_bt_acl_data_sent_cb) {
+ } 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);
+ tud_bt_acl_data_sent_cb((uint16_t) xferred_bytes);
}
}
diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h
index 4f6350839..89a056dc8 100755
--- a/src/class/bth/bth_device.h
+++ b/src/class/bth/bth_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_BTH_DEVICE_H_
-#define _TUSB_BTH_DEVICE_H_
+#ifndef TUSB_BTH_DEVICE_H_
+#define TUSB_BTH_DEVICE_H_
#include <common/tusb_common.h>
#include <device/usbd.h>
@@ -67,23 +67,23 @@ typedef struct TU_ATTR_PACKED
// Part E, 5.4.1.
// Length of the command is from 3 bytes (2 bytes for OpCode,
// 1 byte for parameter total length) to 258.
-TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
+void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
// Invoked when ACL data was received over USB from Bluetooth host.
// Detailed format is described in Bluetooth core specification Vol 2,
// Part E, 5.4.2.
// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
// and 16 bits for data total length) to endpoint size.
-TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
+void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
// Called when event sent with tud_bt_event_send() was delivered to BT stack.
// Controller can release/reuse buffer with Event packet at this point.
-TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes);
+void tud_bt_event_sent_cb(uint16_t sent_bytes);
// Called when ACL data that was sent with tud_bt_acl_data_send()
// was delivered to BT stack.
// Controller can release/reuse buffer with ACL packet at this point.
-TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes);
+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.
@@ -114,4 +114,4 @@ bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t r
}
#endif
-#endif /* _TUSB_BTH_DEVICE_H_ */
+#endif /* TUSB_BTH_DEVICE_H_ */
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index 5cbd658fe..679723ba6 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -29,8 +29,8 @@
* Currently only Abstract Control Model subclass is supported
* @{ */
-#ifndef _TUSB_CDC_H__
-#define _TUSB_CDC_H__
+#ifndef TUSB_CDC_H__
+#define TUSB_CDC_H__
#include "common/tusb_common.h"
@@ -192,6 +192,11 @@ typedef enum {
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
} cdc_line_coding_stopbits_t;
+#define CDC_LINE_CODING_STOP_BITS_TEXT(STOP_BITS) ( \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \
+ (STOP_BITS) == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?" )
+
// 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
@@ -205,22 +210,31 @@ typedef enum {
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, ///< This notification allows the device to notify the host about network connection status.
- CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
+ 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, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
+ 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_notification_request_t;
+} cdc_notify_request_t;
//--------------------------------------------------------------------+
// Class Specific Functional Descriptor (Communication Interface)
@@ -231,8 +245,7 @@ TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
/// Header Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
+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_
@@ -240,8 +253,7 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_header_t;
/// Union Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
+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_
@@ -259,14 +271,13 @@ typedef struct TU_ATTR_PACKED
}
/// Country Selection Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
+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;
+} cdc_desc_func_country_selection_t;
#define cdc_desc_func_country_selection_n_t(no_country) \
struct TU_ATTR_PACKED { \
@@ -283,8 +294,7 @@ typedef struct TU_ATTR_PACKED
/// \brief Call Management Functional Descriptor
/// \details This functional descriptor describes the processing of calls for the Communications Class interface.
-typedef struct TU_ATTR_PACKED
-{
+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_
@@ -298,8 +308,7 @@ typedef struct TU_ATTR_PACKED
uint8_t bDataInterface;
}cdc_desc_func_call_management_t;
-typedef struct TU_ATTR_PACKED
-{
+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
@@ -311,8 +320,7 @@ TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compile
/// 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
-{
+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_
@@ -321,8 +329,7 @@ typedef struct TU_ATTR_PACKED
/// \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
-{
+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_
@@ -384,16 +391,14 @@ typedef struct TU_ATTR_PACKED
}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
// TODO remove
-static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
-{
+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
-{
+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
@@ -402,15 +407,58 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
-typedef struct TU_ATTR_PACKED
-{
- uint16_t dtr : 1;
- uint16_t rts : 1;
- uint16_t : 6;
- uint16_t : 8;
+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) == 2, "size is not correct");
+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
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index 4d19adeb4..babb89952 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -46,103 +46,165 @@
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
typedef struct {
+ uint8_t rhport;
uint8_t itf_num;
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
-
- // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
- uint8_t line_state;
+ uint8_t ep_notify;
+ uint8_t line_state; // Bit 0: DTR, Bit 1: RTS
/*------------- From this point, data is not cleared by bus reset -------------*/
- char wanted_char;
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
+ char wanted_char;
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
-
- uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
+ struct {
+ tu_edpt_stream_t tx;
+ tu_edpt_stream_t rx;
- OSAL_MUTEX_DEF(rx_ff_mutex);
- OSAL_MUTEX_DEF(tx_ff_mutex);
+ uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
+ uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
+ } stream;
} cdcd_interface_t;
-#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, line_coding)
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
+// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
-static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
-CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
+TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf) {
+ (void)itf;
+}
-static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
+TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) {
+ (void)itf;
+ (void)wanted_char;
+}
-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];
+TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf) {
+ (void)itf;
+}
- // Skip if usb is not ready yet
- TU_VERIFY(tud_ready() && p_cdc->ep_out);
+TU_ATTR_WEAK void tud_cdc_notify_complete_cb(uint8_t itf) {
+ (void)itf;
+}
- uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
+TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
+ (void)itf;
+ (void)dtr;
+ (void)rts;
+}
- // 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);
+TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, const cdc_line_coding_t *p_line_coding) {
+ (void)itf;
+ (void)p_line_coding;
+}
- // claim endpoint
- TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out));
+TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
+ (void)itf;
+ (void)duration_ms;
+}
- // fifo can be changed before endpoint is claimed
- available = tu_fifo_remaining(&p_cdc->rx_ff);
+//--------------------------------------------------------------------+
+// 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();
- 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(rhport, p_cdc->ep_out);
- return false;
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_cdc_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_CDC; idx++) {
+ const cdcd_interface_t *p_cdc = &_cdcd_itf[idx];
+ if (ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr ||
+ (ep_addr == p_cdc->ep_notify && ep_addr != 0)) {
+ return idx;
+ }
}
+ return TUSB_INDEX_INVALID_8;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
- TU_VERIFY(driver_cfg);
+ TU_VERIFY(driver_cfg != NULL);
_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;
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ TU_VERIFY(tud_ready());
+ const cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+
+ const bool in_opened = tu_edpt_stream_is_opened(&p_cdc->stream.tx);
+ const bool out_opened = tu_edpt_stream_is_opened(&p_cdc->stream.rx);
+ return in_opened && out_opened;
}
bool tud_cdc_n_connected(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ TU_VERIFY(tud_ready());
// DTR (bit 0) active is considered as connected
- return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
+ return tu_bit_test(_cdcd_itf[itf].line_state, 0);
}
uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
return _cdcd_itf[itf].line_state;
}
-void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t *coding) {
+ TU_VERIFY(itf < CFG_TUD_CDC, );
(*coding) = _cdcd_itf[itf].line_coding;
}
+#if CFG_TUD_CDC_NOTIFY
+bool tud_cdc_n_notify_uart_state (uint8_t itf, const cdc_notify_uart_state_t *state) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ 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) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ 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) {
+ TU_VERIFY(itf < CFG_TUD_CDC, );
_cdcd_itf[itf].wanted_char = wanted;
}
@@ -150,82 +212,53 @@ void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
// READ API
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_available(uint8_t itf) {
- return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ return tu_edpt_stream_read_available(&_cdcd_itf[itf].stream.rx);
}
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
- _prep_out_transaction(itf);
- return num_read;
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_read(p_cdc->rhport, &p_cdc->stream.rx, buffer, bufsize);
}
-bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
- return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
+bool tud_cdc_n_peek(uint8_t itf, uint8_t *chr) {
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ return tu_edpt_stream_peek(&_cdcd_itf[itf].stream.rx, chr);
}
void tud_cdc_n_read_flush(uint8_t itf) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- tu_fifo_clear(&p_cdc->rx_ff);
- _prep_out_transaction(itf);
+ TU_VERIFY(itf < CFG_TUD_CDC, );
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ tu_edpt_stream_clear(&p_cdc->stream.rx);
+ tu_edpt_stream_read_xfer(p_cdc->rhport, &p_cdc->stream.rx);
}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint16_t wr_count = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
-
- // flush if queue more than packet size
- if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
- #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
- || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
- #endif
- ) {
- tud_cdc_n_write_flush(itf);
- }
-
- return wr_count;
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write(p_cdc->rhport, &p_cdc->stream.tx, buffer, bufsize);
}
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
-
- // Skip if usb is not ready yet
- TU_VERIFY(tud_ready(), 0);
-
- // No data to send
- if (!tu_fifo_count(&p_cdc->tx_ff)) {
- return 0;
- }
-
- const uint8_t rhport = 0;
-
- // Claim the endpoint
- TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_in), 0);
-
- // 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(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(rhport, p_cdc->ep_in);
- return 0;
- }
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write_xfer(p_cdc->rhport, &p_cdc->stream.tx);
}
uint32_t tud_cdc_n_write_available(uint8_t itf) {
- return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_write_available(p_cdc->rhport, &p_cdc->stream.tx);
}
bool tud_cdc_n_write_clear(uint8_t itf) {
- return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
+ TU_VERIFY(itf < CFG_TUD_CDC);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ return tu_edpt_stream_clear(&p_cdc->stream.tx);
}
//--------------------------------------------------------------------+
@@ -234,7 +267,8 @@ bool tud_cdc_n_write_clear(uint8_t itf) {
void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
- cdcd_interface_t* p_cdc = &_cdcd_itf[i];
+ cdcd_interface_t *p_cdc = &_cdcd_itf[i];
+ cdcd_epbuf_t *p_epbuf = &_cdcd_epbuf[i];
p_cdc->wanted_char = (char) -1;
@@ -244,44 +278,23 @@ void cdcd_init(void) {
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
- // Config RX fifo
- tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
+ tu_edpt_stream_init(&p_cdc->stream.rx, false, false, false, p_cdc->stream.rx_ff_buf, CFG_TUD_CDC_RX_BUFSIZE,
+ p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
- tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, _cdcd_cfg.tx_overwritabe_if_not_connected);
-
- #if OSAL_MUTEX_REQUIRED
- osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
- osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
- TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
-
- tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
- tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
- #endif
+ tu_edpt_stream_init(&p_cdc->stream.tx, false, true, _cdcd_cfg.tx_overwritabe_if_not_connected,
+ p_cdc->stream.tx_ff_buf, CFG_TUD_CDC_TX_BUFSIZE, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
}
}
bool cdcd_deinit(void) {
- #if OSAL_MUTEX_REQUIRED
- for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
- osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
- osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
-
- if (mutex_rd) {
- osal_mutex_delete(mutex_rd);
- tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
- }
-
- if (mutex_wr) {
- osal_mutex_delete(mutex_wr);
- tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
- }
+ tu_edpt_stream_deinit(&p_cdc->stream.rx);
+ tu_edpt_stream_deinit(&p_cdc->stream.tx);
}
- #endif
-
return true;
}
@@ -290,74 +303,85 @@ void cdcd_reset(uint8_t rhport) {
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
-
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
- if (!_cdcd_cfg.rx_persistent) {
- tu_fifo_clear(&p_cdc->rx_ff);
- }
- if (!_cdcd_cfg.tx_persistent) {
- tu_fifo_clear(&p_cdc->tx_ff);
- }
- tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
+
+ tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, _cdcd_cfg.tx_overwritabe_if_not_connected); // back to default
+ tu_edpt_stream_close(&p_cdc->stream.rx);
+ tu_edpt_stream_close(&p_cdc->stream.tx);
}
}
uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
// Only support ACM subclass
- TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
+ TU_VERIFY(TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass,
+ 0);
- // Find available interface
- cdcd_interface_t* p_cdc;
- uint8_t cdc_id;
- for (cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
- p_cdc = &_cdcd_itf[cdc_id];
- if (p_cdc->ep_in == 0) {
- break;
- }
- }
+ const uint8_t cdc_id = find_cdc_itf(0); // Find available interface
TU_ASSERT(cdc_id < CFG_TUD_CDC, 0);
+ cdcd_interface_t *p_cdc = &_cdcd_itf[cdc_id];
//------------- Control Interface -------------//
+ p_cdc->rhport = rhport;
p_cdc->itf_num = itf_desc->bInterfaceNumber;
- uint16_t drv_len = sizeof(tusb_desc_interface_t);
- const uint8_t* p_desc = tu_desc_next(itf_desc);
+ const uint8_t *p_desc = (const uint8_t *)itf_desc;
+ const uint8_t *desc_end = p_desc + max_len;
- // Communication Functional Descriptors
- while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
- drv_len += tu_desc_len(p_desc);
+ // Skip all class-specific descriptor
+ p_desc = tu_desc_next(itf_desc);
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
p_desc = tu_desc_next(p_desc);
}
+ // notification endpoint (optional)
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
- // notification endpoint
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
-
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
- p_cdc->ep_notif = desc_ep->bEndpointAddress;
+ p_cdc->ep_notify = desc_ep->bEndpointAddress;
- drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
- //------------- Data Interface (if any) -------------//
- if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((const tusb_desc_interface_t*) p_desc)->bInterfaceClass)) {
- // next to endpoint descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ //------------- Data Interface (optional) -------------//
+ if (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) {
+ const tusb_desc_interface_t *data_itf_desc = (const tusb_desc_interface_t *)p_desc;
+ if (TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass) {
+ for (uint8_t e = 0; e < data_itf_desc->bNumEndpoints; e++) {
+ if (!tu_desc_in_bounds(p_desc, desc_end)) {
+ break;
+ }
+ p_desc = tu_desc_next(p_desc);
- // Open endpoint pair
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0);
- drv_len += 2 * sizeof(tusb_desc_endpoint_t);
- }
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p_cdc->stream.tx;
- // Prepare for incoming data
- _prep_out_transaction(cdc_id);
+ tu_edpt_stream_open(stream_tx, desc_ep);
+ if (_cdcd_cfg.tx_persistent) {
+ tu_edpt_stream_write_xfer(rhport, stream_tx); // flush pending data
+ } else {
+ tu_edpt_stream_clear(stream_tx);
+ }
+ } else {
+ tu_edpt_stream_t *stream_rx = &p_cdc->stream.rx;
+
+ tu_edpt_stream_open(stream_rx, desc_ep);
+ if (!_cdcd_cfg.rx_persistent) {
+ tu_edpt_stream_clear(stream_rx);
+ }
+ TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data
+ }
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
- return drv_len;
+ return (uint16_t)(p_desc - (const uint8_t *)itf_desc);
}
// Invoked when a control transfer occurred on an interface of this class
@@ -385,9 +409,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
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) {
- if (tud_cdc_line_coding_cb) {
- tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
- }
+ tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
+ } else {
+ // nothing to do
}
break;
@@ -414,17 +438,15 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
// 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);
+ tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, !dtr);
} else {
- tu_fifo_set_overwritable(&p_cdc->tx_ff, false);
+ tu_fifo_set_overwritable(&p_cdc->stream.tx.ff, false);
}
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
-
- // Invoke callback
- if (tud_cdc_line_state_cb) {
- tud_cdc_line_state_cb(itf, dtr, rts);
- }
+ tud_cdc_line_state_cb(itf, dtr, rts); // invoke callback
+ } else {
+ // nothing to do
}
break;
@@ -433,9 +455,9 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
TU_LOG_DRV(" Send Break\r\n");
- if (tud_cdc_send_break_cb) {
- tud_cdc_send_break_cb(itf, request->wValue);
- }
+ tud_cdc_send_break_cb(itf, request->wValue);
+ } else {
+ // nothing to do
}
break;
@@ -447,64 +469,52 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
}
bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
- (void) result;
-
- uint8_t itf;
- cdcd_interface_t* p_cdc;
+ (void)result;
- // 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)) {
- break;
- }
- }
+ uint8_t itf = find_cdc_itf(ep_addr);
TU_ASSERT(itf < CFG_TUD_CDC);
- cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
+ cdcd_interface_t *p_cdc = &_cdcd_itf[itf];
+ tu_edpt_stream_t *stream_rx = &p_cdc->stream.rx;
+ tu_edpt_stream_t *stream_tx = &p_cdc->stream.tx;
- // Received new data
- if (ep_addr == p_cdc->ep_out) {
- tu_fifo_write_n(&p_cdc->rx_ff, p_epbuf->epout, (uint16_t) xferred_bytes);
+ // Received new data, move to fifo
+ if (ep_addr == stream_rx->ep_addr) {
+ tu_edpt_stream_read_xfer_complete(stream_rx, xferred_bytes);
- // Check for wanted char and invoke callback if needed
- if (tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1)) {
+ // Check for wanted char and invoke wanted callback (multiple times if multiple wanted received)
+ 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)) {
+ if ((p_cdc->wanted_char == (char)stream_rx->ep_buf[i]) && !tu_edpt_stream_empty(stream_rx)) {
tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
}
}
}
- // invoke receive callback (if there is still data)
- if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) {
+ // invoke receive callback if there is still data
+ if (!tu_edpt_stream_empty(stream_rx)) {
tud_cdc_rx_cb(itf);
}
- // prepare for OUT transaction
- _prep_out_transaction(itf);
+ tu_edpt_stream_read_xfer(rhport, stream_rx); // prepare for more data
}
// Data sent to host, we continue to fetch from tx fifo to send.
// Note: This will cause incorrect baudrate set in line coding.
// Though maybe the baudrate is not really important !!!
- if (ep_addr == p_cdc->ep_in) {
+ if (ep_addr == stream_tx->ep_addr) {
// invoke transmit callback to possibly refill tx fifo
- if (tud_cdc_tx_complete_cb) {
- tud_cdc_tx_complete_cb(itf);
- }
+ 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));
- }
- }
+ if (0 == tu_edpt_stream_write_xfer(rhport, stream_tx)) {
+ // If there is no data left, a ZLP should be sent if needed
+ tu_edpt_stream_write_zlp_if_needed(rhport, stream_tx, xferred_bytes);
}
}
- // nothing to do with notif endpoint for now
+ // Sent notification to host
+ if (ep_addr == p_cdc->ep_notify) {
+ tud_cdc_notify_complete_cb(itf);
+ }
return true;
}
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index b653ebd74..6f21af4f3 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -32,6 +32,10 @@
//--------------------------------------------------------------------+
// 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
@@ -49,15 +53,16 @@
// 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
+ bool rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
+ bool tx_persistent : 1; // keep tx fifo data even with reset or disconnect
+ bool tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
} tud_cdc_configure_t;
+TU_VERIFY_STATIC(sizeof(tud_cdc_configure_t) == 1, "size is not correct");
#define TUD_CDC_CONFIGURE_DEFAULT() { \
- .rx_persistent = 0, \
- .tx_persistent = 0, \
- .tx_overwritabe_if_not_connected = 1, \
+ .rx_persistent = false, \
+ .tx_persistent = false, \
+ .tx_overwritabe_if_not_connected = true, \
}
// Configure CDC driver behavior
@@ -126,6 +131,23 @@ 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)
//--------------------------------------------------------------------+
@@ -195,29 +217,32 @@ TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) {
}
//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
+// Application Callback API
//--------------------------------------------------------------------+
// Invoked when received new data
-TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
+void tud_cdc_rx_cb(uint8_t itf);
// Invoked when received `wanted_char`
-TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+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
-TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf);
+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
-TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
// Invoked when line coding is change via SET_LINE_CODING
-TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
+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.
-TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
+void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
@@ -233,4 +258,4 @@ bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t re
}
#endif
-#endif /* _TUSB_CDC_DEVICE_H_ */
+#endif /* TUSB_CDC_DEVICE_H_ */
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index bf245db3f..35717ddf6 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -24,7 +24,7 @@
* This file is part of the TinyUSB stack.
*
* Contribution
- * - Heiko Kuester: CH34x support
+ * - Heiko Kuester: add support of CH34x & PL2303, improve support of FTDI & CP210x
*/
#include "tusb_option.h"
@@ -35,13 +35,19 @@
#include "host/usbh_pvt.h"
#include "cdc_host.h"
+#include "serial/ftdi_sio.h"
+#include "serial/cp210x.h"
+#include "serial/ch34x.h"
+#include "serial/pl2303.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
#ifndef CFG_TUH_CDC_LOG_LEVEL
- #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL
+ #define CFG_TUH_CDC_LOG_LEVEL 2
#endif
-#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \
+ serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__)
//--------------------------------------------------------------------+
// Host CDC Interface
@@ -56,30 +62,40 @@ typedef struct {
uint8_t ep_notif;
uint8_t serial_drid; // Serial Driver ID
bool mounted; // Enumeration is complete
- cdc_acm_capability_t acm_capability;
- TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
- uint8_t line_state; // DTR (bit0), RTS (bit1)
+ struct {
+ TU_ATTR_ALIGNED(4) cdc_line_coding_t coding; // Baudrate, stop bits, parity, data width
+ cdc_line_control_state_t control_state; // DTR, RTS
+ } line, requested_line;
- #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
- cdc_line_coding_t requested_line_coding;
- // 1 byte padding
- #endif
+ tuh_xfer_cb_t user_complete_cb; // required since we handle request internally first
+
+ union {
+ struct {
+ cdc_acm_capability_t capability;
+ } acm;
+
+ #if CFG_TUH_CDC_FTDI
+ ftdi_private_t ftdi;
+ #endif
- tuh_xfer_cb_t user_control_cb;
+ #if CFG_TUH_CDC_PL2303
+ pl2303_private_t pl2303;
+ #endif
+ };
struct {
tu_edpt_stream_t tx;
tu_edpt_stream_t rx;
uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
- uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
+ uint8_t rx_ff_buf[CFG_TUH_CDC_RX_BUFSIZE];
} stream;
} cdch_interface_t;
typedef struct {
TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE);
- TUH_EPBUF_DEF(rx, CFG_TUH_CDC_TX_EPSIZE);
+ TUH_EPBUF_DEF(rx, CFG_TUH_CDC_RX_EPSIZE);
} cdch_epbuf_t;
static cdch_interface_t cdch_data[CFG_TUH_CDC];
@@ -89,58 +105,70 @@ CFG_TUH_MEM_SECTION static cdch_epbuf_t cdch_epbuf[CFG_TUH_CDC];
// Serial Driver
//--------------------------------------------------------------------+
+// General driver
+static void cdch_process_set_config(tuh_xfer_t *xfer);
+static void cdch_process_line_state_on_enum(tuh_xfer_t *xfer); // invoked after set config is processed
+static void cdch_internal_control_complete(tuh_xfer_t *xfer);
+static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer);
+static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer);
+
//------------- ACM prototypes -------------//
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-static void acm_process_config(tuh_xfer_t* xfer);
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
//------------- FTDI prototypes -------------//
#if CFG_TUH_CDC_FTDI
-#include "serial/ftdi_sio.h"
-
static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
+static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t *xfer);
-static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
-static void ftdi_process_config(tuh_xfer_t* xfer);
-
-static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CP210X prototypes -------------//
#if CFG_TUH_CDC_CP210X
-#include "serial/cp210x.h"
-
static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
-static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
-static void cp210x_process_config(tuh_xfer_t* xfer);
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- CH34x prototypes -------------//
#if CFG_TUH_CDC_CH34X
-#include "serial/ch34x.h"
-
static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
-static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len);
-static void ch34x_process_config(tuh_xfer_t* xfer);
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
-static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- PL2303 prototypes -------------//
+#if CFG_TUH_CDC_PL2303
+static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST};
+static const pl2303_type_data_t pl2303_type_data[PL2303_TYPE_COUNT] = {PL2303_TYPE_DATA};
+
+static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+
+static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
#endif
//------------- Common -------------//
@@ -159,31 +187,48 @@ enum {
SERIAL_DRIVER_CH34X,
#endif
+#if CFG_TUH_CDC_PL2303
+ SERIAL_DRIVER_PL2303,
+#endif
+
SERIAL_DRIVER_COUNT
};
+typedef bool (*serial_driver_func_t)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
typedef struct {
uint16_t const (*vid_pid_list)[2];
uint16_t const vid_pid_count;
- bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
- void (*const process_set_config)(tuh_xfer_t* xfer);
- bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
- bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
- bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
- bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
+ bool (*const process_set_config)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer);
+ void (*const request_complete)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer); // internal request complete handler to update line state
+
+ serial_driver_func_t set_control_line_state, set_baudrate, set_data_format, set_line_coding;
+
+ #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL
+ const char * name;
+ #endif
} cdch_serial_driver_t;
+#if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL
+ #define DRIVER_NAME_DECLARE(_str) .name = _str
+#else
+ #define DRIVER_NAME_DECLARE(_str)
+#endif
+
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
{
.vid_pid_list = NULL,
.vid_pid_count = 0,
.open = acm_open,
- .process_set_config = acm_process_config,
+ .process_set_config = acm_process_set_config,
+ .request_complete = acm_internal_control_complete,
.set_control_line_state = acm_set_control_line_state,
- .set_baudrate = acm_set_baudrate,
- .set_data_format = acm_set_data_format,
- .set_line_coding = acm_set_line_coding
+ .set_baudrate = acm_set_line_coding,
+ .set_data_format = acm_set_line_coding,
+ .set_line_coding = acm_set_line_coding,
+ DRIVER_NAME_DECLARE("ACM")
},
#if CFG_TUH_CDC_FTDI
@@ -191,11 +236,13 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = ftdi_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
.open = ftdi_open,
- .process_set_config = ftdi_process_config,
- .set_control_line_state = ftdi_sio_set_modem_ctrl,
- .set_baudrate = ftdi_sio_set_baudrate,
+ .process_set_config = ftdi_proccess_set_config,
+ .request_complete = ftdi_internal_control_complete,
+ .set_control_line_state = ftdi_set_modem_ctrl,
+ .set_baudrate = ftdi_set_baudrate,
.set_data_format = ftdi_set_data_format,
- .set_line_coding = ftdi_set_line_coding
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("FTDI")
},
#endif
@@ -204,11 +251,13 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = cp210x_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
.open = cp210x_open,
- .process_set_config = cp210x_process_config,
+ .process_set_config = cp210x_process_set_config,
+ .request_complete = cp210x_internal_control_complete,
.set_control_line_state = cp210x_set_modem_ctrl,
.set_baudrate = cp210x_set_baudrate,
.set_data_format = cp210x_set_data_format,
- .set_line_coding = cp210x_set_line_coding
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CP210x")
},
#endif
@@ -217,13 +266,31 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = ch34x_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
.open = ch34x_open,
- .process_set_config = ch34x_process_config,
+ .process_set_config = ch34x_process_set_config,
+ .request_complete = ch34x_internal_control_complete,
+
.set_control_line_state = ch34x_set_modem_ctrl,
.set_baudrate = ch34x_set_baudrate,
.set_data_format = ch34x_set_data_format,
- .set_line_coding = ch34x_set_line_coding
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CH34x")
},
#endif
+
+ #if CFG_TUH_CDC_PL2303
+ {
+ .vid_pid_list = pl2303_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
+ .open = pl2303_open,
+ .process_set_config = pl2303_process_set_config,
+ .request_complete = pl2303_internal_control_complete,
+ .set_control_line_state = pl2303_set_modem_ctrl,
+ .set_baudrate = pl2303_set_line_coding,
+ .set_data_format = pl2303_set_line_coding,
+ .set_line_coding = pl2303_set_line_coding,
+ DRIVER_NAME_DECLARE("PL2303")
+ }
+ #endif
};
TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
@@ -231,18 +298,22 @@ TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial d
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
+static bool open_ep_stream_pair(cdch_interface_t *p_cdc, const tusb_desc_endpoint_t *desc_ep);
-static inline cdch_interface_t* get_itf(uint8_t idx) {
+TU_ATTR_ALWAYS_INLINE static inline cdch_interface_t * get_itf(uint8_t idx) {
TU_ASSERT(idx < CFG_TUH_CDC, NULL);
- cdch_interface_t* p_cdc = &cdch_data[idx];
-
+ cdch_interface_t * p_cdc = &cdch_data[idx];
return (p_cdc->daddr != 0) ? p_cdc : NULL;
}
+TU_ATTR_ALWAYS_INLINE static inline uint8_t get_idx_by_ptr(cdch_interface_t* p_cdc) {
+ return (uint8_t) (p_cdc - cdch_data);
+}
+
static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
- cdch_interface_t* p_cdc = &cdch_data[i];
- if ( (p_cdc->daddr == daddr) &&
+ cdch_interface_t * p_cdc = &cdch_data[i];
+ if ((p_cdc->daddr == daddr) &&
(ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) {
return i;
}
@@ -251,15 +322,67 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
return TUSB_INDEX_INVALID_8;
}
-static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) {
+// determine the interface from the completed transfer
+static cdch_interface_t* get_itf_by_xfer(const tuh_xfer_t * xfer) {
+ TU_VERIFY(xfer->daddr != 0, NULL);
+ for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
+ cdch_interface_t * p_cdc = &cdch_data[i];
+ if (p_cdc->daddr == xfer->daddr) {
+ switch (p_cdc->serial_drid) {
+ #if CFG_TUH_CDC_CP210X
+ case SERIAL_DRIVER_CP210X:
+ #endif
+ case SERIAL_DRIVER_ACM: {
+ // Driver use wIndex for bInterfaceNumber
+ const uint8_t itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ if (p_cdc->bInterfaceNumber == itf_num) {
+ return p_cdc;
+ }
+ break;
+ }
+
+ #if CFG_TUH_CDC_FTDI
+ case SERIAL_DRIVER_FTDI: {
+ // FTDI uses wIndex for channel number, if channel is 0 then it is the default channel
+ const uint8_t channel = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ if (p_cdc->ftdi.channel == 0 || p_cdc->ftdi.channel == channel) {
+ return p_cdc;
+ }
+ break;
+ }
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ case SERIAL_DRIVER_CH34X:
+ // ch34x has only one interface
+ return p_cdc;
+ #endif
+
+ #if CFG_TUH_CDC_PL2303
+ case SERIAL_DRIVER_PL2303:
+ // pl2303 has only one interface
+ return p_cdc;
+ #endif
+
+ default:
+ break; // unknown driver
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static cdch_interface_t * make_new_itf(uint8_t daddr, tusb_desc_interface_t const * itf_desc) {
for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
if (cdch_data[i].daddr == 0) {
- cdch_interface_t* p_cdc = &cdch_data[i];
+ cdch_interface_t * p_cdc = &cdch_data[i];
p_cdc->daddr = daddr;
p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber;
p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol;
- p_cdc->line_state = 0;
+ p_cdc->line.coding = (cdc_line_coding_t) { 0, 0, 0, 0 };
+ p_cdc->line.control_state.value = 0;
return p_cdc;
}
}
@@ -267,9 +390,24 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const
return NULL;
}
-static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep);
-static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num);
-static void cdch_internal_control_complete(tuh_xfer_t* xfer);
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tuh_cdc_mount_cb(uint8_t idx) {
+ (void) idx;
+}
+
+TU_ATTR_WEAK void tuh_cdc_umount_cb(uint8_t idx) {
+ (void) idx;
+}
+
+TU_ATTR_WEAK void tuh_cdc_rx_cb(uint8_t idx) {
+ (void) idx;
+}
+
+TU_ATTR_WEAK void tuh_cdc_tx_complete_cb(uint8_t idx) {
+ (void) idx;
+}
//--------------------------------------------------------------------+
// APPLICATION API
@@ -277,21 +415,20 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer);
uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) {
for (uint8_t i = 0; i < CFG_TUH_CDC; i++) {
- const cdch_interface_t* p_cdc = &cdch_data[i];
- if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
+ const cdch_interface_t * p_cdc = &cdch_data[i];
+ if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) { return i; }
}
-
return TUSB_INDEX_INVALID_8;
}
-bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
- cdch_interface_t* p_cdc = get_itf(idx);
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t * info) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && info);
info->daddr = p_cdc->daddr;
- // re-construct descriptor
- tusb_desc_interface_t* desc = &info->desc;
+ // re-construct interface descriptor
+ tusb_desc_interface_t * desc = &info->desc;
desc->bLength = sizeof(tusb_desc_interface_t);
desc->bDescriptorType = TUSB_DESC_INTERFACE;
@@ -307,31 +444,22 @@ bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
}
bool tuh_cdc_mounted(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
return p_cdc->mounted;
}
-bool tuh_cdc_get_dtr(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
- TU_VERIFY(p_cdc);
-
- return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
-}
-
-bool tuh_cdc_get_rts(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t* line_state) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
- return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
+ *line_state = p_cdc->line.control_state.value;
+ return true;
}
-bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) {
- cdch_interface_t* p_cdc = get_itf(idx);
+bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t * line_coding) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
- *line_coding = p_cdc->line_coding;
-
+ *line_coding = p_cdc->line.coding;
return true;
}
@@ -339,31 +467,27 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding)
// Write
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) {
- cdch_interface_t* p_cdc = get_itf(idx);
+uint32_t tuh_cdc_write(uint8_t idx, void const * buffer, uint32_t bufsize) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_write(p_cdc->daddr, &p_cdc->stream.tx, buffer, bufsize);
}
uint32_t tuh_cdc_write_flush(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_write_xfer(p_cdc->daddr, &p_cdc->stream.tx);
}
bool tuh_cdc_write_clear(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_clear(&p_cdc->stream.tx);
}
uint32_t tuh_cdc_write_available(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_write_available(p_cdc->daddr, &p_cdc->stream.tx);
}
@@ -371,33 +495,30 @@ uint32_t tuh_cdc_write_available(uint8_t idx) {
// Read
//--------------------------------------------------------------------+
-uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) {
- cdch_interface_t* p_cdc = get_itf(idx);
+uint32_t tuh_cdc_read (uint8_t idx, void * buffer, uint32_t bufsize) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_read(p_cdc->daddr, &p_cdc->stream.rx, buffer, bufsize);
}
uint32_t tuh_cdc_read_available(uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_read_available(&p_cdc->stream.rx);
}
-bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) {
- cdch_interface_t* p_cdc = get_itf(idx);
+bool tuh_cdc_peek(uint8_t idx, uint8_t * ch) {
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
-
return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
}
bool tuh_cdc_read_clear (uint8_t idx) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc);
bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx);
- tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
+ (void)tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
return ret;
}
@@ -405,218 +526,115 @@ bool tuh_cdc_read_clear (uint8_t idx) {
// Control Endpoint API
//--------------------------------------------------------------------+
-static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) {
- uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t* p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
- uint16_t const value = tu_le16toh(xfer->setup->wValue);
-
- if (xfer->result == XFER_RESULT_SUCCESS) {
- switch (p_cdc->serial_drid) {
- case SERIAL_DRIVER_ACM:
- switch (xfer->setup->bRequest) {
- case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- p_cdc->line_state = (uint8_t) value;
- break;
-
- case CDC_REQUEST_SET_LINE_CODING: {
- uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
- memcpy(&p_cdc->line_coding, xfer->buffer, len);
- break;
- }
-
- default: break;
- }
- break;
-
- #if CFG_TUH_CDC_FTDI
- case SERIAL_DRIVER_FTDI:
- switch (xfer->setup->bRequest) {
- case FTDI_SIO_MODEM_CTRL:
- p_cdc->line_state = (uint8_t) value;
- break;
-
- case FTDI_SIO_SET_BAUD_RATE:
- p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
- break;
-
- default: break;
- }
- break;
- #endif
-
- #if CFG_TUH_CDC_CP210X
- case SERIAL_DRIVER_CP210X:
- switch(xfer->setup->bRequest) {
- case CP210X_SET_MHS:
- p_cdc->line_state = (uint8_t) value;
- break;
-
- case CP210X_SET_BAUDRATE: {
- uint32_t baudrate;
- memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
- p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
- break;
- }
-
- default: break;
- }
- break;
- #endif
-
- #if CFG_TUH_CDC_CH34X
- case SERIAL_DRIVER_CH34X:
- switch (xfer->setup->bRequest) {
- case CH34X_REQ_WRITE_REG:
- // register write request
- switch (value) {
- case CH34X_REG16_DIVISOR_PRESCALER:
- // baudrate
- p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
- break;
-
- case CH32X_REG16_LCR2_LCR:
- // data format
- p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
- p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
- p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
- break;
-
- default: break;
- }
- break;
-
- case CH34X_REQ_MODEM_CTRL: {
- // set modem controls RTS/DTR request. Note: signals are inverted
- uint16_t const modem_signal = ~value;
- if (modem_signal & CH34X_BIT_RTS) {
- p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
- } else {
- p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
- }
-
- if (modem_signal & CH34X_BIT_DTR) {
- p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
- } else {
- p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
- }
- break;
- }
-
- default: break;
- }
- break;
- #endif
-
- default: break;
- }
- }
-
- xfer->complete_cb = p_cdc->user_control_cb;
- if (xfer->complete_cb) {
- xfer->complete_cb(xfer);
- }
-}
-
-// internal control complete to update state such as line state, encoding
-static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- process_internal_control_complete(xfer, itf_num);
-}
-
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t * p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+ TU_LOG_CDC(p_cdc, "set control line state dtr = %u rts = %u", p_cdc->requested_line.control_state.dtr, p_cdc->requested_line.control_state.rts);
+ const cdch_serial_driver_t * driver = &serial_drivers[p_cdc->serial_drid];
- if (complete_cb) {
- return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
- } else {
- // blocking
- xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
-
- if (user_data) {
- // user_data is not NULL, return result via user_data
- *((xfer_result_t*) user_data) = result;
- }
+ p_cdc->requested_line.control_state.value = (uint8_t) line_state;
+ p_cdc->user_complete_cb = complete_cb;
+ TU_VERIFY(driver->set_control_line_state(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data));
- TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
- p_cdc->line_state = (uint8_t) line_state;
- return true;
+ if (!complete_cb) {
+ // blocking, update line state if request was successful
+ p_cdc->line.control_state.value = (uint8_t) line_state;
}
+
+ return true;
}
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t *p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+ TU_LOG_CDC(p_cdc, "set baudrate %lu", baudrate);
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
- if (complete_cb) {
- return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
- } else {
- // blocking
- xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
+ p_cdc->requested_line = p_cdc->line; // keep current line coding
+ p_cdc->requested_line.coding.bit_rate = baudrate;
+ p_cdc->user_complete_cb = complete_cb;
+ TU_VERIFY(driver->set_baudrate(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data));
- if (user_data) {
- // user_data is not NULL, return result via user_data
- *((xfer_result_t*) user_data) = result;
- }
-
- TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
- p_cdc->line_coding.bit_rate = baudrate;
- return true;
+ if (!complete_cb) {
+ p_cdc->line.coding.bit_rate = baudrate;
}
+
+ return true;
}
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) {
- cdch_interface_t* p_cdc = get_itf(idx);
+ cdch_interface_t *p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+ TU_LOG_CDC(p_cdc, "set data format %u%c%s",
+ data_bits, CDC_LINE_CODING_PARITY_CHAR(parity),
+ CDC_LINE_CODING_STOP_BITS_TEXT(stop_bits));
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
- if (complete_cb) {
- return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data);
- } else {
- // blocking
- xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result);
+ p_cdc->requested_line = p_cdc->line; // keep current line coding
+ p_cdc->requested_line.coding.stop_bits = stop_bits;
+ p_cdc->requested_line.coding.parity = parity;
+ p_cdc->requested_line.coding.data_bits = data_bits;
- if (user_data) {
- // user_data is not NULL, return result via user_data
- *((xfer_result_t*) user_data) = result;
- }
+ p_cdc->user_complete_cb = complete_cb;
+ TU_VERIFY(driver->set_data_format(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data));
- TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
- p_cdc->line_coding.stop_bits = stop_bits;
- p_cdc->line_coding.parity = parity;
- p_cdc->line_coding.data_bits = data_bits;
- return true;
+ if (!complete_cb) {
+ // blocking
+ p_cdc->line.coding.stop_bits = stop_bits;
+ p_cdc->line.coding.parity = parity;
+ p_cdc->line.coding.data_bits = data_bits;
}
+
+ return true;
}
-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) {
- cdch_interface_t* p_cdc = get_itf(idx);
+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) {
+ cdch_interface_t *p_cdc = get_itf(idx);
TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+ TU_LOG_CDC(p_cdc, "set line coding %lu %u%c%s",
+ line_coding->bit_rate, line_coding->data_bits,
+ CDC_LINE_CODING_PARITY_CHAR(line_coding->parity),
+ CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits));
+ cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid];
+ p_cdc->requested_line.coding = *line_coding;
+ p_cdc->user_complete_cb = complete_cb;
- if ( complete_cb ) {
- return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data);
- } else {
- // blocking
- xfer_result_t result = XFER_RESULT_INVALID;
- bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+ if (driver->set_line_coding != NULL) {
+ // driver support set_line_coding request
+ TU_VERIFY(driver->set_line_coding(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data));
- if (user_data) {
- // user_data is not NULL, return result via user_data
- *((xfer_result_t*) user_data) = result;
+ if (!complete_cb) {
+ p_cdc->line.coding = *line_coding;
}
+ } else {
+ // driver does not support set_line_coding and need 2 stage to set baudrate and data format separately
+ if (complete_cb != NULL) {
+ // non-blocking
+ TU_VERIFY(driver->set_baudrate(p_cdc, cdch_set_line_coding_stage1_baudrate_complete, user_data));
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
- TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
- p_cdc->line_coding = *line_coding;
- return true;
+ TU_VERIFY(driver->set_baudrate(p_cdc, NULL, (uintptr_t) &result));
+ if (user_data != 0) {
+ *((xfer_result_t *) user_data) = result;
+ }
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate; // update baudrate
+
+ result = XFER_RESULT_INVALID;
+ TU_VERIFY(driver->set_data_format(p_cdc, NULL, (uintptr_t) &result));
+ if (user_data != 0) {
+ *((xfer_result_t *) user_data) = result;
+ }
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line.coding = p_cdc->requested_line.coding; // update data format
+ }
}
+
+ return true;
}
//--------------------------------------------------------------------+
@@ -627,15 +645,12 @@ bool cdch_init(void) {
TU_LOG_DRV("sizeof(cdch_interface_t) = %u\r\n", sizeof(cdch_interface_t));
tu_memclr(cdch_data, sizeof(cdch_data));
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
- cdch_interface_t* p_cdc = &cdch_data[i];
- cdch_epbuf_t* epbuf = &cdch_epbuf[i];
- tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false,
- p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
- epbuf->tx, CFG_TUH_CDC_TX_EPSIZE);
-
- tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
- p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
- epbuf->rx, CFG_TUH_CDC_RX_EPSIZE);
+ cdch_interface_t *p_cdc = &cdch_data[i];
+ cdch_epbuf_t *epbuf = &cdch_epbuf[i];
+ TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+ epbuf->tx, CFG_TUH_CDC_TX_EPSIZE));
+ TU_ASSERT(tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, p_cdc->stream.rx_ff_buf,
+ CFG_TUH_CDC_RX_BUFSIZE, epbuf->rx, CFG_TUH_CDC_RX_EPSIZE));
}
return true;
@@ -643,25 +658,21 @@ bool cdch_init(void) {
bool cdch_deinit(void) {
for (size_t i = 0; i < CFG_TUH_CDC; i++) {
- cdch_interface_t* p_cdc = &cdch_data[i];
- tu_edpt_stream_deinit(&p_cdc->stream.tx);
- tu_edpt_stream_deinit(&p_cdc->stream.rx);
+ cdch_interface_t *p_cdc = &cdch_data[i];
+ (void)tu_edpt_stream_deinit(&p_cdc->stream.tx);
+ (void)tu_edpt_stream_deinit(&p_cdc->stream.rx);
}
return true;
}
void cdch_close(uint8_t daddr) {
for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
- cdch_interface_t* p_cdc = &cdch_data[idx];
+ cdch_interface_t *p_cdc = &cdch_data[idx];
if (p_cdc->daddr == daddr) {
- TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
-
- // Invoke application callback
- if (tuh_cdc_umount_cb) {
- tuh_cdc_umount_cb(idx);
- }
+ TU_LOG_CDC(p_cdc, "close");
+ tuh_cdc_umount_cb(idx); // invoke callback
- p_cdc->daddr = 0;
+ p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
p_cdc->mounted = false;
tu_edpt_stream_close(&p_cdc->stream.tx);
@@ -675,43 +686,40 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_VERIFY(event == XFER_RESULT_SUCCESS);
uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
- cdch_interface_t * p_cdc = get_itf(idx);
+ cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT(p_cdc);
- if ( ep_addr == p_cdc->stream.tx.ep_addr ) {
- // invoke tx complete callback to possibly refill tx fifo
- if (tuh_cdc_tx_complete_cb) {
- tuh_cdc_tx_complete_cb(idx);
- }
+ if (ep_addr == p_cdc->stream.tx.ep_addr) {
+ tuh_cdc_tx_complete_cb(idx); // invoke transmit complete callback
- if ( 0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx) ) {
+ if (0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx)) {
// If there is no data left, a ZLP should be sent if:
// - xferred_bytes is multiple of EP Packet size and not zero
- tu_edpt_stream_write_zlp_if_needed(daddr, &p_cdc->stream.tx, xferred_bytes);
+ (void)tu_edpt_stream_write_zlp_if_needed(daddr, &p_cdc->stream.tx, xferred_bytes);
}
- } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+ } else if (ep_addr == p_cdc->stream.rx.ep_addr) {
#if CFG_TUH_CDC_FTDI
- if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI && xferred_bytes > 2) {
+ if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
// FTDI reserve 2 bytes for status
// uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
- tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf+2, xferred_bytes-2);
- }else
+ if (xferred_bytes > 2) {
+ tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf + 2, xferred_bytes - 2);
+
+ tuh_cdc_rx_cb(idx); // invoke receive callback
+ }
+ } else
#endif
{
tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
- }
-
- // invoke receive callback
- if (tuh_cdc_rx_cb) {
- tuh_cdc_rx_cb(idx);
+ tuh_cdc_rx_cb(idx); // invoke receive callback
}
// prepare for next transfer if needed
tu_edpt_stream_read_xfer(daddr, &p_cdc->stream.rx);
- }else if ( ep_addr == p_cdc->ep_notif ) {
+ } else if (ep_addr == p_cdc->ep_notif) {
// TODO handle notification endpoint
- }else {
- TU_ASSERT(false);
+ } else {
+ return false;
}
return true;
@@ -720,20 +728,17 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
-
-static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) {
+static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t const *desc_ep) {
for (size_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
+ tu_edpt_stream_t *stream =
+ (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) ? &p_cdc->stream.rx : &p_cdc->stream.tx;
+ tu_edpt_stream_open(stream, desc_ep);
+ tu_edpt_stream_clear(stream);
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- tu_edpt_stream_open(&p_cdc->stream.rx, desc_ep);
- } else {
- tu_edpt_stream_open(&p_cdc->stream.tx, desc_ep);
- }
-
- desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep);
+ desc_ep = (const tusb_desc_endpoint_t *)tu_desc_next(desc_ep);
}
return true;
@@ -741,10 +746,9 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co
bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) rhport;
-
// For CDC: only support ACM subclass
// Note: Protocol 0xFF can be RNDIS device
- if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
return acm_open(daddr, itf_desc, max_len);
} else if (SERIAL_DRIVER_COUNT > 1 &&
@@ -753,175 +757,226 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d
TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
- cdch_serial_driver_t const* driver = &serial_drivers[dr];
+ const cdch_serial_driver_t *driver = &serial_drivers[dr];
for (size_t i = 0; i < driver->vid_pid_count; i++) {
if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
- return driver->open(daddr, itf_desc, max_len);
+ const bool ret = driver->open(daddr, itf_desc, max_len);
+ TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, ret ? "OK" : "FAILED");
+ return ret;
}
}
}
+ } else {
+ // not supported class
}
return false;
}
-static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
- TU_LOG_DRV("CDCh Set Configure complete\r\n");
- p_cdc->mounted = true;
- if (tuh_cdc_mount_cb) {
- tuh_cdc_mount_cb(idx);
- }
-
- // Prepare for incoming data
- tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
-
- // notify usbh that driver enumeration is complete
- usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
-}
-
bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
+ uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
+ cdch_interface_t *p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ TU_LOG_CDC(p_cdc, "set config");
- // fake transfer to kick-off process
+ // fake transfer to kick-off process_set_config()
tuh_xfer_t xfer;
- xfer.daddr = daddr;
+ xfer.daddr = daddr;
xfer.result = XFER_RESULT_SUCCESS;
- xfer.setup = &request;
- xfer.user_data = 0; // initial state
+ xfer.setup = &request;
+ xfer.user_data = 0; // initial state 0
+ cdch_process_set_config(&xfer);
- uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
- cdch_interface_t * p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
-
- serial_drivers[p_cdc->serial_drid].process_set_config(&xfer);
return true;
}
-//--------------------------------------------------------------------+
-// ACM
-//--------------------------------------------------------------------+
+static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
+ if (success) {
+ const uint8_t idx = get_idx_by_ptr(p_cdc);
+ p_cdc->mounted = true;
+ tuh_cdc_mount_cb(idx);
+ // Prepare for incoming data
+ tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
+ } else {
+ // clear the interface entry
+ p_cdc->daddr = 0;
+ p_cdc->bInterfaceNumber = 0;
+ }
-enum {
- CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
- CONFIG_ACM_SET_LINE_CODING,
- CONFIG_ACM_COMPLETE,
-};
+ // notify usbh that driver enumeration is complete
+ const uint8_t itf_offset = (p_cdc->serial_drid == SERIAL_DRIVER_ACM) ? 1 : 0;
+ usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset);
+}
-static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
- uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+static void cdch_process_set_config(tuh_xfer_t *xfer) {
+ cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
+ TU_LOG_DRV(" state = %u\r\n", xfer->user_data);
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
- cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
- p_cdc->serial_drid = SERIAL_DRIVER_ACM;
+ if (!driver->process_set_config(p_cdc, xfer)) {
+ set_config_complete(p_cdc, false);
+ }
+}
- //------------- Control Interface -------------//
- uint8_t const* p_desc = tu_desc_next(itf_desc);
+// return false if there is no active transfer
+static bool set_line_state_on_enum(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ enum {
+ ENUM_SET_LINE_CODING = 0,
+ ENUM_SET_LINE_CONTROL,
+ ENUM_SET_LINE_COMPLETE,
+ };
+ const uint8_t idx = get_idx_by_ptr(p_cdc);
+ const uintptr_t state = xfer->user_data;
- // Communication Functional Descriptors
- while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
- if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
- // save ACM bmCapabilities
- p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities;
+ switch (state) {
+ case ENUM_SET_LINE_CODING: {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ #if CFG_TUH_CDC_CH34X
+ // ch34x already set line coding in serial init
+ if (p_cdc->serial_drid != SERIAL_DRIVER_CH34X)
+ #endif
+ {
+ const cdc_line_coding_t line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(tuh_cdc_set_line_coding(idx, &line_coding,
+ cdch_process_line_state_on_enum, ENUM_SET_LINE_CONTROL));
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
}
- p_desc = tu_desc_next(p_desc);
+ case ENUM_SET_LINE_CONTROL:
+ #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_ASSERT(tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM,
+ cdch_process_line_state_on_enum, ENUM_SET_LINE_COMPLETE));
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+
+ case ENUM_SET_LINE_COMPLETE:
+ set_config_complete(p_cdc, true);
+ break;
+
+ default:
+ return false;
}
- // Open notification endpoint of control interface if any
- if (itf_desc->bNumEndpoints == 1) {
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
- tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
+ return true;
+}
- TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
- p_cdc->ep_notif = desc_ep->bEndpointAddress;
+static void cdch_process_line_state_on_enum(tuh_xfer_t *xfer) {
+ cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
+ if (xfer->result != XFER_RESULT_SUCCESS || !set_line_state_on_enum(p_cdc, xfer)) {
+ set_config_complete(p_cdc, false);
+ }
+}
- p_desc = tu_desc_next(p_desc);
+
+static void cdch_internal_control_complete(tuh_xfer_t *xfer) {
+ cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
+ TU_LOG_DRV(" request result = %u\r\n", xfer->result);
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
+ driver->request_complete(p_cdc, xfer);
+
+ // Invoke application callback
+ xfer->complete_cb = p_cdc->user_complete_cb;
+ if (xfer->complete_cb != NULL) {
+ xfer->complete_cb(xfer);
}
+}
- //------------- Data Interface (if any) -------------//
- if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
- // next to endpoint descriptor
- p_desc = tu_desc_next(p_desc);
+static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer) {
+ cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
+ TU_LOG_DRV(" stage1 set baudrate result = %u\r\n", xfer->result);
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
- // data endpoints expected to be in pairs
- TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc));
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate; // update baudrate
+ TU_ASSERT(driver->set_data_format(p_cdc, cdch_set_line_coding_stage2_data_format_complete, xfer->user_data),);
+ } else {
+ xfer->complete_cb = p_cdc->user_complete_cb;
+ if (xfer->complete_cb != NULL) {
+ xfer->complete_cb(xfer);
+ }
}
+}
- return true;
+static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer) {
+ cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
+ TU_LOG_DRV(" stage2 set data format result = %u\r\n", xfer->result);
+
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ p_cdc->line.coding = p_cdc->requested_line.coding; // update data format
+ }
+
+ xfer->complete_cb = p_cdc->user_complete_cb;
+ if (xfer->complete_cb != NULL) {
+ xfer->complete_cb(xfer);
+ }
}
-static void acm_process_config(tuh_xfer_t* xfer) {
- uintptr_t const state = xfer->user_data;
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t* p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc,);
+//--------------------------------------------------------------------+
+// ACM
+//--------------------------------------------------------------------+
- switch (state) {
- case CONFIG_ACM_SET_CONTROL_LINE_STATE:
- #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- if (p_cdc->acm_capability.support_line_request) {
- TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),);
- break;
- }
- #endif
- TU_ATTR_FALLTHROUGH;
+// internal control complete to update state such as line state, encoding
+static void acm_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_VERIFY (xfer->result == XFER_RESULT_SUCCESS,);
+ const tusb_control_request_t * setup = xfer->setup;
- case CONFIG_ACM_SET_LINE_CODING:
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- if (p_cdc->acm_capability.support_line_request) {
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),);
- break;
- }
- #endif
- TU_ATTR_FALLTHROUGH;
+ switch (setup->bRequest) {
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ p_cdc->line.control_state = p_cdc->requested_line.control_state;
+ break;
- case CONFIG_ACM_COMPLETE:
- // itf_num+1 to account for data interface as well
- set_config_complete(p_cdc, idx, itf_num + 1);
+ case CDC_REQUEST_SET_LINE_CODING:
+ p_cdc->line.coding = p_cdc->requested_line.coding;
break;
default:
- break;
+ break; // unknown request
}
}
-static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_VERIFY(p_cdc->acm_capability.support_line_request);
- TU_LOG_DRV("CDC ACM Set Control Line State\r\n");
+static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm.capability.support_line_request != 0);
- tusb_control_request_t const request = {
+ const tusb_control_request_t request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
- .wValue = tu_htole16(line_state),
+ .wValue = tu_htole16((uint16_t) p_cdc->requested_line.control_state.value),
.wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
.wLength = 0
};
- p_cdc->user_control_cb = complete_cb;
-
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
- .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .complete_cb = complete_cb,
.user_data = user_data
};
- TU_ASSERT(tuh_control_xfer(&xfer));
- return true;
+ return tuh_control_xfer(&xfer);
}
-static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC ACM Set Line Conding\r\n");
+static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm.capability.support_line_request != 0);
+ TU_VERIFY((p_cdc->requested_line.coding.data_bits >= 5 && p_cdc->requested_line.coding.data_bits <= 8) ||
+ p_cdc->requested_line.coding.data_bits == 16);
tusb_control_request_t const request = {
.bmRequestType_bit = {
@@ -931,46 +986,93 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const
},
.bRequest = CDC_REQUEST_SET_LINE_CODING,
.wValue = 0,
- .wIndex = tu_htole16(p_cdc->bInterfaceNumber),
- .wLength = tu_htole16(sizeof(cdc_line_coding_t))
+ .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
+ .wLength = tu_htole16((uint16_t) sizeof(cdc_line_coding_t))
};
// use usbh enum buf to hold line coding since user line_coding variable does not live long enough
- uint8_t* enum_buf = usbh_get_enum_buf();
- memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t));
+ uint8_t *enum_buf = usbh_get_enum_buf();
+ memcpy(enum_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t));
- p_cdc->user_control_cb = complete_cb;
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
.buffer = enum_buf,
- .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .complete_cb = complete_cb,
.user_data = user_data
};
- TU_ASSERT(tuh_control_xfer(&xfer));
- return true;
+ return tuh_control_xfer(&xfer);
}
-static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC ACM Set Data Format\r\n");
+//------------- Enumeration -------------//
+enum {
+ CONFIG_ACM_COMPLETE = 0
+};
+
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ uint8_t const *p_desc_end = ((uint8_t const *) itf_desc) + max_len;
+
+ cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ p_cdc->serial_drid = SERIAL_DRIVER_ACM;
- cdc_line_coding_t line_coding;
- line_coding.bit_rate = p_cdc->line_coding.bit_rate;
- line_coding.stop_bits = stop_bits;
- line_coding.parity = parity;
- line_coding.data_bits = data_bits;
+ //------------- Control Interface -------------//
+ uint8_t const *p_desc = tu_desc_next(itf_desc);
+
+ // Communication Functional Descriptors
+ while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+ if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
+ // save ACM bmCapabilities
+ p_cdc->acm.capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
- return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+ // Open notification endpoint of control interface if any
+ if (itf_desc->bNumEndpoints == 1) {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass)) {
+ // next to endpoint descriptor
+ p_desc = tu_desc_next(p_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc));
+ }
+
+ return true;
}
-static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_VERIFY(p_cdc->acm_capability.support_line_request);
- cdc_line_coding_t line_coding = p_cdc->line_coding;
- line_coding.bit_rate = baudrate;
- return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
+ (void) p_cdc;
+ const uintptr_t state = xfer->user_data;
+
+ switch (state) {
+ case CONFIG_ACM_COMPLETE: {
+ xfer->user_data = 0; // kick-off set line state on enum
+ cdch_process_line_state_on_enum(xfer);
+ break;
+ }
+
+ default:
+ return false; // invalid state
+ }
+
+ return true;
}
//--------------------------------------------------------------------+
@@ -978,50 +1080,26 @@ static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfe
//--------------------------------------------------------------------+
#if CFG_TUH_CDC_FTDI
-enum {
- CONFIG_FTDI_RESET = 0,
- CONFIG_FTDI_MODEM_CTRL,
- CONFIG_FTDI_SET_BAUDRATE,
- CONFIG_FTDI_SET_DATA,
- CONFIG_FTDI_COMPLETE
-};
-
-static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
- // FTDI Interface includes 1 vendor interface + 2 bulk endpoints
- TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
-
- cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
-
- TU_LOG_DRV("FTDI opened\r\n");
- p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
-
- // endpoint pair
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+static bool ftdi_determine_type(cdch_interface_t *p_cdc);
+static uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc);
- // data endpoints expected to be in pairs
- return open_ep_stream_pair(p_cdc, desc_ep);
-}
+//------------- Control Request -------------//
// set request without data
-static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- tusb_control_request_t const request = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_DEVICE,
- .type = TUSB_REQ_TYPE_VENDOR,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = command,
- .wValue = tu_htole16(value),
- .wIndex = 0,
- .wLength = 0
+static bool ftdi_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t requesttype,
+ uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request_setup = {
+ .bmRequestType = requesttype,
+ .bRequest = request,
+ .wValue = tu_htole16(value),
+ .wIndex = tu_htole16(index),
+ .wLength = 0
};
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
- .setup = &request,
+ .setup = &request_setup,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
@@ -1030,162 +1108,382 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1
return tuh_control_xfer(&xfer);
}
-static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
+#ifdef CFG_TUH_CDC_FTDI_LATENCY
+static int8_t ftdi_write_latency_timer(cdch_interface_t * p_cdc, uint16_t latency,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ if (p_cdc->ftdi.chip_type == FTDI_SIO /* || p_cdc->ftdi.chip_type == FT232A */ )
+ return FTDI_NOT_POSSIBLE;
+ return ftdi_set_request(p_cdc, FTDI_SIO_SET_LATENCY_TIMER_REQUEST, FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ latency, p_cdc->ftdi.channel, complete_cb, user_data) ? FTDI_REQUESTED : FTDI_FAIL;
}
+#endif
-static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- (void) p_cdc;
- (void) stop_bits;
- (void) parity;
- (void) data_bits;
- (void) complete_cb;
- (void) user_data;
- // TODO not implemented yet
- return false;
+static inline bool ftdi_sio_reset(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ftdi_set_request(p_cdc, FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, FTDI_SIO_RESET_SIO,
+ p_cdc->ftdi.channel, complete_cb, user_data);
}
-static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- (void) p_cdc;
- (void) line_coding;
- (void) complete_cb;
- (void) user_data;
- // TODO not implemented yet
- return false;
-}
-static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
- p_cdc->user_control_cb = complete_cb;
- TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
- complete_cb ? cdch_internal_control_complete : NULL, user_data));
- return true;
-}
+//------------- Driver API -------------//
-static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
- const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
- uint32_t divisor;
+// internal control complete to update state such as line state, line_coding
+static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t *xfer) {
+ const tusb_control_request_t * setup = xfer->setup;
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ if (setup->bRequest == FTDI_SIO_SET_MODEM_CTRL_REQUEST &&
+ setup->bmRequestType == FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE ) {
+ p_cdc->line.control_state = p_cdc->requested_line.control_state;
+ }
+ if (setup->bRequest == FTDI_SIO_SET_DATA_REQUEST &&
+ setup->bmRequestType == FTDI_SIO_SET_DATA_REQUEST_TYPE ) {
+ p_cdc->line.coding.stop_bits = p_cdc->requested_line.coding.stop_bits;
+ p_cdc->line.coding.parity = p_cdc->requested_line.coding.parity;
+ p_cdc->line.coding.data_bits = p_cdc->requested_line.coding.data_bits;
+ }
+ if (setup->bRequest == FTDI_SIO_SET_BAUDRATE_REQUEST &&
+ setup->bmRequestType == FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE ) {
+ p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate;
+ }
+ }
+}
- /* divisor shifted 3 bits to the left */
- uint32_t divisor3 = base / (2 * baud);
- divisor = (divisor3 >> 3);
- divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
+static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->requested_line.coding.data_bits >= 7 && p_cdc->requested_line.coding.data_bits <= 8, 0);
+ uint16_t value = (uint16_t) ((p_cdc->requested_line.coding.data_bits & 0xfUL) | // data bit quantity is stored in bits 0-3
+ (p_cdc->requested_line.coding.parity & 0x7UL) << 8 | // parity is stored in bits 8-10, same coding
+ (p_cdc->requested_line.coding.stop_bits & 0x3UL) << 11); // stop bits quantity is stored in bits 11-12, same coding
+ // not each FTDI supports 1.5 stop bits
+ return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE,
+ value, p_cdc->ftdi.channel, complete_cb, user_data);
+}
- /* Deal with special cases for highest baud rates. */
- if (divisor == 1) { /* 1.0 */
- divisor = 0;
- }
- else if (divisor == 0x4001) { /* 1.5 */
- divisor = 1;
+static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint32_t index_value = ftdi_get_divisor(p_cdc);
+ TU_VERIFY(index_value != 0);
+ uint16_t value = (uint16_t) index_value;
+ uint16_t index = (uint16_t) (index_value >> 16);
+ if (p_cdc->ftdi.channel != 0) {
+ index = (uint16_t) ((index << 8) | p_cdc->ftdi.channel);
}
- return divisor;
+ return ftdi_set_request(p_cdc, FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+ value, index, complete_cb, user_data);
}
-static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
- return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
+static bool ftdi_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint16_t line_state = (uint16_t) ((p_cdc->requested_line.control_state.dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) |
+ (p_cdc->requested_line.control_state.rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW));
+ return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ line_state, p_cdc->ftdi.channel, complete_cb ? cdch_internal_control_complete : NULL, user_data);
}
-static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
- TU_LOG_DRV("CDC FTDI Set BaudRate = %" PRIu32 ", divisor = 0x%04x\r\n", baudrate, divisor);
+//------------- Enumeration -------------//
+enum {
+ CONFIG_FTDI_DETERMINE_TYPE = 0,
+ CONFIG_FTDI_WRITE_LATENCY,
+ CONFIG_FTDI_SIO_RESET,
+ CONFIG_FTDI_FLOW_CONTROL,
+ CONFIG_FTDI_COMPLETE
+};
- p_cdc->user_control_cb = complete_cb;
- p_cdc->requested_line_coding.bit_rate = baudrate;
- TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
- complete_cb ? cdch_internal_control_complete : NULL, user_data));
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ // FTDI Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff &&
+ itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
- return true;
-}
+ cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
-static void ftdi_process_config(tuh_xfer_t* xfer) {
- uintptr_t const state = xfer->user_data;
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t * p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
+ p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
- switch(state) {
- // Note may need to read FTDI eeprom
- case CONFIG_FTDI_RESET:
- TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),);
- break;
+ // endpoint pair
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
- case CONFIG_FTDI_MODEM_CTRL:
- #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
- break;
- #else
+ /*
+ * NOTE: Some customers have programmed FT232R/FT245R devices
+ * with an endpoint size of 0 - not good.
+ */
+ TU_ASSERT(desc_ep->wMaxPacketSize != 0);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
+}
+
+static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
+ const uintptr_t state = xfer->user_data;
+ switch (state) {
+ // from here sequence overtaken from Linux Kernel function ftdi_port_probe()
+ case CONFIG_FTDI_DETERMINE_TYPE:
+ // determine type
+ if (p_cdc->bInterfaceNumber == 0) {
+ TU_ASSERT(ftdi_determine_type(p_cdc));
+ } else {
+ // other interfaces have same type as interface 0
+ uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0);
+ cdch_interface_t const *p_cdc_itf0 = get_itf(idx_itf0);
+ TU_ASSERT(p_cdc_itf0);
+ p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type;
+ }
TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_FTDI_WRITE_LATENCY:
+ #ifdef CFG_TUH_CDC_FTDI_LATENCY
+ int8_t result = ftdi_write_latency_timer(p_cdc, CFG_TUH_CDC_FTDI_LATENCY, ftdi_process_config,
+ CONFIG_FTDI_SIO_RESET);
+ TU_ASSERT(result != FTDI_FAIL);
+ if (result == FTDI_REQUESTED) {
+ break;
+ }// else FTDI_NOT_POSSIBLE => continue directly with next state
#endif
+ TU_ATTR_FALLTHROUGH;
- case CONFIG_FTDI_SET_BAUDRATE: {
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),);
+ // from here sequence overtaken from Linux Kernel function ftdi_open()
+ case CONFIG_FTDI_SIO_RESET:
+ TU_ASSERT(ftdi_sio_reset(p_cdc, cdch_process_set_config, CONFIG_FTDI_FLOW_CONTROL));
break;
- #else
- TU_ATTR_FALLTHROUGH;
- #endif
- }
- case CONFIG_FTDI_SET_DATA: {
- #if 0 // TODO set data format
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),);
+ case CONFIG_FTDI_FLOW_CONTROL:
+ // disable flow control
+ TU_ASSERT(ftdi_set_request(p_cdc, FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, FTDI_SIO_DISABLE_FLOW_CTRL,
+ p_cdc->ftdi.channel, cdch_process_set_config, CONFIG_FTDI_COMPLETE));
break;
- #endif
- #endif
- TU_ATTR_FALLTHROUGH;
+ case CONFIG_FTDI_COMPLETE: {
+ xfer->user_data = 0; // kick-off set line state on enum
+ cdch_process_line_state_on_enum(xfer);
+ break;
}
- case CONFIG_FTDI_COMPLETE:
- set_config_complete(p_cdc, idx, itf_num);
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+//------------- Helper -------------//
+
+static bool ftdi_determine_type(cdch_interface_t *p_cdc) {
+ tusb_desc_device_t desc_dev;
+ TU_VERIFY(tuh_descriptor_get_device_local(p_cdc->daddr, &desc_dev));
+ uint16_t const version = desc_dev.bcdDevice;
+ uint8_t const itf_num = p_cdc->bInterfaceNumber;
+
+ p_cdc->ftdi.chip_type = FTDI_UNKNOWN;
+
+ /* Assume Hi-Speed type */
+ p_cdc->ftdi.channel = CHANNEL_A + itf_num;
+
+ switch (version) {
+ case 0x200:
+ // FT232A not supported to keep it simple (no extra _read_latency_timer()) not testable
+ // p_cdc->ftdi.chip_type = FT232A;
+ // p_cdc->ftdi.baud_base = 48000000 / 2;
+ // p_cdc->ftdi.channel = 0;
+ // /*
+ // * FT232B devices have a bug where bcdDevice gets set to 0x200
+ // * when iSerialNumber is 0. Assume it is an FT232B in case the
+ // * latency timer is readable.
+ // */
+ // if (desc->iSerialNumber == 0 &&
+ // _read_latency_timer(port) >= 0) {
+ // p_cdc->ftdi.chip_type = FTDI_FT232B;
+ // }
break;
+ case 0x400 : p_cdc->ftdi.chip_type = FTDI_FT232B; p_cdc->ftdi.channel = 0; break;
+ case 0x500 : p_cdc->ftdi.chip_type = FTDI_FT2232C; break;
+ case 0x600 : p_cdc->ftdi.chip_type = FTDI_FT232R; p_cdc->ftdi.channel = 0; break;
+ case 0x700 : p_cdc->ftdi.chip_type = FTDI_FT2232H; break;
+ case 0x800 : p_cdc->ftdi.chip_type = FTDI_FT4232H; break;
+ case 0x900 : p_cdc->ftdi.chip_type = FTDI_FT232H; break;
+ case 0x1000: p_cdc->ftdi.chip_type = FTDI_FTX; break;
+ case 0x2800: p_cdc->ftdi.chip_type = FTDI_FT2233HP; break;
+ case 0x2900: p_cdc->ftdi.chip_type = FTDI_FT4233HP; break;
+ case 0x3000: p_cdc->ftdi.chip_type = FTDI_FT2232HP; break;
+ case 0x3100: p_cdc->ftdi.chip_type = FTDI_FT4232HP; break;
+ case 0x3200: p_cdc->ftdi.chip_type = FTDI_FT233HP; break;
+ case 0x3300: p_cdc->ftdi.chip_type = FTDI_FT232HP; break;
+ case 0x3600: p_cdc->ftdi.chip_type = FTDI_FT4232HA; break;
+
default:
+ if (version < 0x200) {
+ p_cdc->ftdi.chip_type = FTDI_SIO;
+ p_cdc->ftdi.channel = 0;
+ }
break;
}
+
+ #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL
+ const char * ftdi_chip_name[] = { FTDI_CHIP_NAMES };
+ TU_LOG_CDC(p_cdc, "%s detected (bcdDevice = 0x%04x)",
+ ftdi_chip_name[p_cdc->ftdi.chip_type], version);
+ #endif
+
+ return (p_cdc->ftdi.chip_type != FTDI_UNKNOWN);
}
-#endif
+// FT232A not supported
+//static uint32_t ftdi_232am_baud_base_to_divisor(uint32_t baud, uint32_t base)
+//{
+// uint32_t divisor;
+// /* divisor shifted 3 bits to the left */
+// uint32_t divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
+// if ((divisor3 & 0x7) == 7)
+// divisor3++; /* round x.7/8 up to x+1 */
+// divisor = divisor3 >> 3;
+// divisor3 &= 0x7;
+// if (divisor3 == 1)
+// divisor |= 0xc000; /* +0.125 */
+// else if (divisor3 >= 4)
+// divisor |= 0x4000; /* +0.5 */
+// else if (divisor3 != 0)
+// divisor |= 0x8000; /* +0.25 */
+// else if (divisor == 1)
+// divisor = 0; /* special case for maximum baud rate */
+// return divisor;
+//}
-//--------------------------------------------------------------------+
-// CP210x
-//--------------------------------------------------------------------+
+// FT232A not supported
+//static inline uint32_t ftdi_232am_baud_to_divisor(uint32_t baud)
+//{
+// return ftdi_232am_baud_base_to_divisor(baud, (uint32_t) 48000000);
+//}
-#if CFG_TUH_CDC_CP210X
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
+ uint8_t divfrac[8] = {0, 3, 2, 4, 1, 5, 6, 7};
+ uint32_t divisor;
+ /* divisor shifted 3 bits to the left */
+ uint32_t divisor3 = tu_div_round_nearest(base, 2 * baud);
+ divisor = divisor3 >> 3;
+ divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) /* 1.0 */ {
+ divisor = 0;
+ } else if (divisor == 0x4001) /* 1.5 */ {
+ divisor = 1;
+ } else {
+ // nothing to do
+ }
+ return divisor;
+}
-enum {
- CONFIG_CP210X_IFC_ENABLE = 0,
- CONFIG_CP210X_SET_BAUDRATE,
- CONFIG_CP210X_SET_LINE_CTL,
- CONFIG_CP210X_SET_DTR_RTS,
- CONFIG_CP210X_COMPLETE
-};
+static inline uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
+ return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
+}
-static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
- // CP210x Interface includes 1 vendor interface + 2 bulk endpoints
- TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
- TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
+static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base) {
+ static const unsigned char divfrac[8] = {0, 3, 2, 4, 1, 5, 6, 7};
+ uint32_t divisor;
+ uint32_t divisor3;
- cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY(p_cdc);
+ /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+ divisor3 = tu_div_round_nearest(8 * base, 10 * baud);
- TU_LOG_DRV("CP210x opened\r\n");
- p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
+ divisor = divisor3 >> 3;
+ divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) /* 1.0 */ {
+ divisor = 0;
+ } else if (divisor == 0x4001) /* 1.5 */ {
+ divisor = 1;
+ } else {
+ // nothing to do
+ }
- // endpoint pair
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+ /* Set this bit to turn off a divide by 2.5 on baud rate generator
+ * This enables baud rates up to 12Mbaud but cannot reach below 1200
+ * baud with this bit set */
+ divisor |= 0x00020000;
+ return divisor;
+}
- // data endpoints expected to be in pairs
- return open_ep_stream_pair(p_cdc, desc_ep);
+static inline uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud) {
+ return ftdi_2232h_baud_base_to_divisor(baud, (uint32_t) 120000000);
+}
+
+static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) {
+ uint32_t baud = p_cdc->requested_line.coding.bit_rate;
+ uint32_t div_value = 0;
+ TU_VERIFY(baud != 0);
+
+ switch (p_cdc->ftdi.chip_type) {
+ case FTDI_UNKNOWN:
+ return 0;
+ case FTDI_SIO:
+ switch (baud) {
+ case 300: div_value = ftdi_sio_b300; break;
+ case 600: div_value = ftdi_sio_b600; break;
+ case 1200: div_value = ftdi_sio_b1200; break;
+ case 2400: div_value = ftdi_sio_b2400; break;
+ case 4800: div_value = ftdi_sio_b4800; break;
+ case 9600: div_value = ftdi_sio_b9600; break;
+ case 19200: div_value = ftdi_sio_b19200; break;
+ case 38400: div_value = ftdi_sio_b38400; break;
+ case 57600: div_value = ftdi_sio_b57600; break;
+ case 115200: div_value = ftdi_sio_b115200; break;
+ default:
+ // Baudrate not supported
+ return 0;
+ break;
+ }
+ break;
+ // FT232A not supported
+ // case FT232A:
+ // if (baud <= 3000000) {
+ // div_value = ftdi_232am_baud_to_divisor(baud);
+ // } else {
+ // // Baud rate too high!
+ // baud = 9600;
+ // div_value = ftdi_232am_baud_to_divisor(9600);
+ // div_okay = false;
+ // }
+ // break;
+ case FTDI_FT232B:
+ case FTDI_FT2232C:
+ case FTDI_FT232R:
+ case FTDI_FTX:
+ TU_VERIFY(baud <= 3000000); // else Baud rate too high!
+ div_value = ftdi_232bm_baud_to_divisor(baud);
+ break;
+ case FTDI_FT232H:
+ case FTDI_FT2232H:
+ case FTDI_FT4232H:
+ case FTDI_FT4232HA:
+ case FTDI_FT232HP:
+ case FTDI_FT233HP:
+ case FTDI_FT2232HP:
+ case FTDI_FT2233HP:
+ case FTDI_FT4232HP:
+ case FTDI_FT4233HP:
+ default:
+ TU_VERIFY(baud <= 12000000); // else Baud rate too high!
+ if (baud >= 1200) {
+ div_value = ftdi_2232h_baud_to_divisor(baud);
+ } else {
+ div_value = ftdi_232bm_baud_to_divisor(baud);
+ }
+ break;
+ }
+
+ TU_LOG_CDC(p_cdc, "Baudrate divisor = 0x%lu", div_value);
+
+ return div_value;
}
-static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+#endif
+
+//--------------------------------------------------------------------+
+// CP210x
+//--------------------------------------------------------------------+
+#if CFG_TUH_CDC_CP210X
+
+//------------- Control Request -------------//
+
+static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16_t value,
+ uint8_t * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
@@ -1194,12 +1492,12 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_
},
.bRequest = command,
.wValue = tu_htole16(value),
- .wIndex = p_cdc->bInterfaceNumber,
+ .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
.wLength = tu_htole16(length)
};
// use usbh enum buf since application variable does not live long enough
- uint8_t* enum_buf = NULL;
+ uint8_t * enum_buf = NULL;
if (buffer && length > 0) {
enum_buf = usbh_get_enum_buf();
@@ -1218,91 +1516,104 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_
return tuh_control_xfer(&xfer);
}
-static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline bool cp210x_ifc_enable(cdch_interface_t *p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
}
-static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- // TODO implement later
- (void) p_cdc;
- (void) line_coding;
- (void) complete_cb;
- (void) user_data;
- return false;
+TU_ATTR_ALWAYS_INLINE static inline bool cp210x_set_mhs(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // CP210x has the same bit coding
+ return cp210x_set_request(p_cdc, CP210X_SET_MHS,
+ (uint16_t) (CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line.control_state.value),
+ NULL, 0, complete_cb, user_data);
}
-static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC CP210x Set BaudRate = %" PRIu32 "\r\n", baudrate);
- uint32_t baud_le = tu_htole32(baudrate);
- p_cdc->user_control_cb = complete_cb;
- return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
- complete_cb ? cdch_internal_control_complete : NULL, user_data);
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_VERIFY(xfer->result == XFER_RESULT_SUCCESS,);
+ switch (xfer->setup->bRequest) {
+ case CP210X_SET_MHS:
+ p_cdc->line.control_state = p_cdc->requested_line.control_state;
+ break;
+
+ case CP210X_SET_LINE_CTL:
+ p_cdc->line.coding.stop_bits = p_cdc->requested_line.coding.stop_bits;
+ p_cdc->line.coding.parity = p_cdc->requested_line.coding.parity;
+ p_cdc->line.coding.data_bits = p_cdc->requested_line.coding.data_bits;
+ break;
+
+ case CP210X_SET_BAUDRATE:
+ p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate;
+ break;
+
+ default:
+ break; // unsupported request
+ }
}
-static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- (void) p_cdc;
- (void) stop_bits;
- (void) parity;
- (void) data_bits;
- (void) complete_cb;
- (void) user_data;
- // TODO not implemented yet
- return false;
+static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support"
+ uint32_t baud_le = tu_htole32(p_cdc->requested_line.coding.bit_rate);
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data);
}
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
- p_cdc->user_control_cb = complete_cb;
- return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
- complete_cb ? cdch_internal_control_complete : NULL, user_data);
+static bool cp210x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->requested_line.coding.data_bits >= 5 && p_cdc->requested_line.coding.data_bits <= 8, 0);
+ uint16_t lcr = (uint16_t) ((p_cdc->requested_line.coding.data_bits & 0xfUL) << 8 | // data bit quantity is stored in bits 8-11
+ (p_cdc->requested_line.coding.parity & 0xfUL) << 4 | // parity is stored in bits 4-7, same coding
+ (p_cdc->requested_line.coding.stop_bits & 0xfUL)); // parity is stored in bits 0-3, same coding
+
+ return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data);
}
-static void cp210x_process_config(tuh_xfer_t* xfer) {
- uintptr_t const state = xfer->user_data;
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t *p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc,);
+static bool cp210x_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return cp210x_set_mhs(p_cdc, complete_cb, user_data);
+}
- switch (state) {
- case CONFIG_CP210X_IFC_ENABLE:
- TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),);
- break;
+//------------- Enumeration -------------//
- case CONFIG_CP210X_SET_BAUDRATE: {
- #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),);
- break;
- #else
- TU_ATTR_FALLTHROUGH;
- #endif
- }
+enum {
+ CONFIG_CP210X_IFC_ENABLE = 0,
+ CONFIG_CP210X_COMPLETE
+};
- case CONFIG_CP210X_SET_LINE_CTL: {
- #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- break;
- #else
- TU_ATTR_FALLTHROUGH;
- #endif
- }
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ // CP210x Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
+}
- case CONFIG_CP210X_SET_DTR_RTS:
- #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
- TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
+ const uintptr_t state = xfer->user_data;
+
+ switch (state) {
+ case CONFIG_CP210X_IFC_ENABLE:
+ TU_ASSERT(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cdch_process_set_config, CONFIG_CP210X_COMPLETE));
break;
- #else
- TU_ATTR_FALLTHROUGH;
- #endif
case CONFIG_CP210X_COMPLETE:
- set_config_complete(p_cdc, idx, itf_num);
+ xfer->user_data = 0;// kick-off set line state on enum
+ cdch_process_line_state_on_enum(xfer);
break;
- default: break;
+ default:
+ return false;
}
+
+ return true;
}
#endif
@@ -1313,13 +1624,14 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
#if CFG_TUH_CDC_CH34X
-static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
-static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+static uint8_t ch34x_get_lcr(cdch_interface_t *p_cdc);
+static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t *p_cdc);
-//------------- control request -------------//
+//------------- Control Request -------------//
-static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value,
- uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_t request,
+ uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request_setup = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_DEVICE,
@@ -1327,13 +1639,13 @@ static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_
.direction = direction & 0x01u
},
.bRequest = request,
- .wValue = tu_htole16 (value),
- .wIndex = tu_htole16 (index),
- .wLength = tu_htole16 (length)
+ .wValue = tu_htole16(value),
+ .wIndex = tu_htole16(index),
+ .wLength = tu_htole16(length)
};
// use usbh enum buf since application variable does not live long enough
- uint8_t* enum_buf = NULL;
+ uint8_t *enum_buf = NULL;
if (buffer && length > 0) {
enum_buf = usbh_get_enum_buf();
@@ -1354,164 +1666,103 @@ static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_
return tuh_control_xfer(&xfer);
}
-static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_out(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data);
}
-static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
- uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_in(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
complete_cb, user_data);
}
-static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline bool ch34x_write_reg(cdch_interface_t *p_cdc, uint16_t reg, uint16_t reg_value,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
}
-//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+//static bool ch34x_read_reg_request ( cdch_interface_t * p_cdc, uint16_t reg,
// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
//{
// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
//}
-static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
- TU_VERIFY(div_ps);
- TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
- complete_cb, user_data));
- return true;
-}
-
//------------- Driver API -------------//
// internal control complete to update state such as line state, encoding
-static void ch34x_control_complete(tuh_xfer_t* xfer) {
- // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
- process_internal_control_complete(xfer, 0);
-}
-
-static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- p_cdc->requested_line_coding.stop_bits = stop_bits;
- p_cdc->requested_line_coding.parity = parity;
- p_cdc->requested_line_coding.data_bits = data_bits;
+static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_VERIFY(xfer->result == XFER_RESULT_SUCCESS,);
+ switch (xfer->setup->bRequest) {
+ case CH34X_REQ_WRITE_REG:
+ // register write request
+ switch (tu_le16toh(xfer->setup->wValue)) {
+ case CH34X_REG16_DIVISOR_PRESCALER:
+ // baudrate
+ p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate;
+ break;
- uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
- TU_VERIFY(lcr);
- TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
- complete_cb ? ch34x_control_complete : NULL, user_data));
- return true;
-}
+ case CH32X_REG16_LCR2_LCR:
+ // data format
+ p_cdc->line.coding.stop_bits = p_cdc->requested_line.coding.stop_bits;
+ p_cdc->line.coding.parity = p_cdc->requested_line.coding.parity;
+ p_cdc->line.coding.data_bits = p_cdc->requested_line.coding.data_bits;
+ break;
-static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- p_cdc->requested_line_coding.bit_rate = baudrate;
- p_cdc->user_control_cb = complete_cb;
- TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate,
- complete_cb ? ch34x_control_complete : NULL, user_data));
- return true;
-}
+ default:
+ break; // unsupported
+ }
+ break;
-static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) {
- // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
- uint8_t const itf_num = 0;
- uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t* p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
+ case CH34X_REQ_MODEM_CTRL:
+ p_cdc->line.control_state = p_cdc->requested_line.control_state;
+ break;
- if (xfer->result == XFER_RESULT_SUCCESS) {
- // stage 1 success, continue to stage 2
- p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
- TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity,
- p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), );
- } else {
- // stage 1 failed, notify user
- xfer->complete_cb = p_cdc->user_control_cb;
- if (xfer->complete_cb) {
- xfer->complete_cb(xfer);
- }
+ default:
+ break; // unsupported request
}
}
-// 2 stages: set baudrate (stage1) + set data format (stage2)
-static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- p_cdc->requested_line_coding = *line_coding;
- p_cdc->user_control_cb = complete_cb;
-
- if (complete_cb) {
- // stage 1 set baudrate
- TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate,
- ch34x_set_line_coding_stage1_complete, user_data));
- } else {
- // sync call
- xfer_result_t result;
-
- // stage 1 set baudrate
- TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result));
- TU_VERIFY(result == XFER_RESULT_SUCCESS);
- p_cdc->line_coding.bit_rate = line_coding->bit_rate;
-
- // stage 2 set data format
- TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits,
- NULL, (uintptr_t) &result));
- TU_VERIFY(result == XFER_RESULT_SUCCESS);
- p_cdc->line_coding.stop_bits = line_coding->stop_bits;
- p_cdc->line_coding.parity = line_coding->parity;
- p_cdc->line_coding.data_bits = line_coding->data_bits;
-
- // update transfer result, user_data is expected to point to xfer_result_t
- if (user_data) {
- *((xfer_result_t*) user_data) = result;
- }
- }
-
- return true;
+static bool ch34x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ const uint8_t lcr = ch34x_get_lcr(p_cdc);
+ TU_VERIFY(lcr > 0);
+ return ch34x_write_reg(p_cdc, CH32X_REG16_LCR2_LCR, lcr, complete_cb, user_data);
}
-static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint8_t control = 0;
- if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
- control |= CH34X_BIT_RTS;
- }
- if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
- control |= CH34X_BIT_DTR;
- }
+static bool ch34x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ const uint16_t div_ps = ch34x_get_divisor_prescaler(p_cdc);
+ TU_VERIFY(div_ps > 0);
+ return ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, complete_cb, user_data);
+}
+static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
// CH34x signals are inverted
- control = ~control;
-
- p_cdc->user_control_cb = complete_cb;
- TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
- complete_cb ? ch34x_control_complete : NULL, user_data));
- return true;
+ uint8_t control = ~((p_cdc->requested_line.control_state.rts ? CH34X_BIT_RTS : 0) |
+ (p_cdc->requested_line.control_state.dtr ? CH34X_BIT_DTR : 0));
+ return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data);
}
//------------- Enumeration -------------//
+
enum {
CONFIG_CH34X_READ_VERSION = 0,
CONFIG_CH34X_SERIAL_INIT,
CONFIG_CH34X_SPECIAL_REG_WRITE,
CONFIG_CH34X_FLOW_CONTROL,
- CONFIG_CH34X_MODEM_CONTROL,
CONFIG_CH34X_COMPLETE
};
-static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
// CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
- TU_VERIFY (itf_desc->bNumEndpoints == 3);
- TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+ TU_VERIFY(itf_desc->bNumEndpoints == 3);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
- cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
- TU_VERIFY (p_cdc);
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
- TU_LOG_DRV ("CH34x opened\r\n");
p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
- tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc);
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
// data endpoints expected to be in pairs
TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
@@ -1526,69 +1777,66 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin
return true;
}
-static void ch34x_process_config(tuh_xfer_t* xfer) {
- // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
- uint8_t const itf_num = 0;
- uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t* p_cdc = get_itf(idx);
- uintptr_t const state = xfer->user_data;
- uint8_t buffer[2]; // TODO remove
- TU_ASSERT (p_cdc,);
- TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,);
+static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
+ const uintptr_t state = xfer->user_data;
switch (state) {
- case CONFIG_CH34X_READ_VERSION:
- TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
- TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
+ case CONFIG_CH34X_READ_VERSION: {
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, enum_buf, 2,
+ cdch_process_set_config, CONFIG_CH34X_SERIAL_INIT));
break;
+ }
case CONFIG_CH34X_SERIAL_INIT: {
// handle version read data, set CH34x line coding (incl. baudrate)
uint8_t const version = xfer->buffer[0];
- TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
- // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
- TU_ASSERT (version >= 0x30,);
- // init CH34x with line coding
- cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
- uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
- TU_ASSERT(div_ps, );
- uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
- TU_ASSERT(lcr, );
- TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
- ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+ TU_LOG_CDC(p_cdc, "Chip Version = 0x%02x", version);
+ // only versions >= 0x30 are tested, below 0x30 seems having other programming
+ // see drivers from WCH vendor, Linux kernel and FreeBSD
+ if (version >= 0x30) {
+ // init CH34x with line coding
+ p_cdc->requested_line.coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc);
+ uint8_t const lcr = ch34x_get_lcr(p_cdc);
+ TU_ASSERT(div_ps != 0 && lcr != 0);
+ TU_ASSERT(ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+ cdch_process_set_config, CONFIG_CH34X_SPECIAL_REG_WRITE));
+ }
break;
}
case CONFIG_CH34X_SPECIAL_REG_WRITE:
// overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
- p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X);
- TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
+ p_cdc->line.coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ TU_ASSERT(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007,
+ cdch_process_set_config, CONFIG_CH34X_FLOW_CONTROL));
break;
case CONFIG_CH34X_FLOW_CONTROL:
// no hardware flow control
- TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
- break;
-
- case CONFIG_CH34X_MODEM_CONTROL:
- // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
- TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
+ TU_ASSERT(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000,
+ cdch_process_set_config, CONFIG_CH34X_COMPLETE));
break;
case CONFIG_CH34X_COMPLETE:
- set_config_complete(p_cdc, idx, itf_num);
+ xfer->user_data = 0; // kick-off set line state on enum
+ cdch_process_line_state_on_enum(xfer);
break;
default:
- TU_ASSERT (false,);
- break;
+ return false;
}
+
+ return true;
}
-//------------- CH34x helper -------------//
+//------------- Helper -------------//
// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
-static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
+static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t *p_cdc) {
+ uint32_t const baval = p_cdc->requested_line.coding.bit_rate;
uint8_t a;
uint8_t b;
uint32_t c;
@@ -1633,16 +1881,20 @@ static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
// reg divisor = a, reg prescaler = b
// According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
// otherwise the chip will buffer data.
- return (uint16_t) ((uint16_t)a << 8 | 0x80 | b);
+ return (uint16_t) ((uint16_t) a << 8 | 0x80 | b);
}
// calculate lcr value from data coding
-static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+static uint8_t ch34x_get_lcr(cdch_interface_t *p_cdc) {
+ uint8_t const stop_bits = p_cdc->requested_line.coding.stop_bits;
+ uint8_t const parity = p_cdc->requested_line.coding.parity;
+ uint8_t const data_bits = p_cdc->requested_line.coding.data_bits;
+
uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
- TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0);
+ TU_VERIFY(data_bits >= 5 && data_bits <= 8);
lcr |= (uint8_t) (data_bits - 5);
- switch(parity) {
+ switch (parity) {
case CDC_LINE_CODING_PARITY_NONE:
break;
@@ -1662,11 +1914,12 @@ static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bit
lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
break;
- default: break;
+ default:
+ break; // invalid parity
}
// 1.5 stop bits not supported
- TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+ TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5);
if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
lcr |= CH34X_LCR_STOP_BITS_2;
}
@@ -1674,7 +1927,616 @@ static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bit
return lcr;
}
-
#endif // CFG_TUH_CDC_CH34X
+//--------------------------------------------------------------------+
+// PL2303
+//--------------------------------------------------------------------+
+#if CFG_TUH_CDC_PL2303
+
+static pl2303_type_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step);
+static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]);
+
+//------------- Control Request -------------//
+static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t requesttype,
+ uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request_setup = {
+ .bmRequestType = requesttype,
+ .bRequest = request,
+ .wValue = tu_htole16(value),
+ .wIndex = tu_htole16(index),
+ .wLength = tu_htole16(length)
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t *enum_buf = NULL;
+
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ if (request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
+ }
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request_setup,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
+}
+
+static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value, uint8_t *buf,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t request = p_cdc->pl2303.type == PL2303_TYPE_HXN ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
+ return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, complete_cb, user_data);
+}
+
+static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_t index,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t request = p_cdc->pl2303.type == PL2303_TYPE_HXN ? PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST;
+ return pl2303_set_request(p_cdc, request, PL2303_VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, complete_cb, user_data);
+}
+
+static inline bool pl2303_supports_hx_status(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t buf = 0;
+ return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0,
+ &buf, 1, complete_cb, user_data);
+}
+
+//static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) {
+// return pl2303_set_request(p_cdc, PL2303_GET_LINE_REQUEST, PL2303_GET_LINE_REQUEST_TYPE, 0, 0, buf, PL2303_LINE_CODING_BUFSIZE);
+//}
+
+//static bool pl2303_set_break(cdch_interface_t * p_cdc, bool enable) {
+// uint16_t state = enable ? PL2303_BREAK_ON : PL2303_BREAK_OFF;
+// return pl2303_set_request(p_cdc, PL2303_BREAK_REQUEST, PL2303_BREAK_REQUEST_TYPE, state, 0, NULL, 0);
+//}
+
+static inline bool
+pl2303_clear_halt(cdch_interface_t *p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ /* we don't care if it wasn't halted first. in fact some devices
+ * (like some ibmcam model 1 units) seem to expect hosts to make
+ * this request for iso endpoints, which can't halt!
+ */
+ return pl2303_set_request(
+ p_cdc, TUSB_REQ_CLEAR_FEATURE, PL2303_CLEAR_HALT_REQUEST_TYPE, TUSB_REQ_FEATURE_EDPT_HALT, endp, NULL, 0,
+ complete_cb, user_data);
+}
+
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void pl2303_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ TU_VERIFY(xfer->result == XFER_RESULT_SUCCESS,);
+ if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST &&
+ xfer->setup->bmRequestType == PL2303_SET_LINE_REQUEST_TYPE) {
+ p_cdc->line.coding = p_cdc->requested_line.coding;
+ }
+ if (xfer->setup->bRequest == PL2303_SET_CONTROL_REQUEST &&
+ xfer->setup->bmRequestType == PL2303_SET_CONTROL_REQUEST_TYPE) {
+ p_cdc->line.control_state = p_cdc->requested_line.control_state;
+ }
+}
+
+static bool pl2303_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // the caller has to precheck, that the new line coding different than the current, else false returned
+ uint8_t buf[PL2303_LINE_CODING_BUFSIZE];
+ /*
+ * Some PL2303 are known to lose bytes if you change serial settings
+ * even to the same values as before. Thus we actually need to filter
+ * in this specific case.
+ */
+ TU_VERIFY(p_cdc->requested_line.coding.data_bits != p_cdc->line.coding.data_bits ||
+ p_cdc->requested_line.coding.stop_bits != p_cdc->line.coding.stop_bits ||
+ p_cdc->requested_line.coding.parity != p_cdc->line.coding.parity ||
+ p_cdc->requested_line.coding.bit_rate != p_cdc->line.coding.bit_rate );
+
+ /* For reference buf[6] data bits value */
+ TU_VERIFY(p_cdc->requested_line.coding.data_bits >= 5 && p_cdc->requested_line.coding.data_bits <= 8, 0);
+ buf[6] = p_cdc->requested_line.coding.data_bits;
+
+ /* For reference buf[0]:buf[3] baud rate value */
+ TU_VERIFY(pl2303_encode_baud_rate(p_cdc, &buf[0]));
+
+ /* For reference buf[4]=0 is 1 stop bits */
+ /* For reference buf[4]=1 is 1.5 stop bits */
+ /* For reference buf[4]=2 is 2 stop bits */
+ buf[4] = p_cdc->requested_line.coding.stop_bits; // PL2303 has the same coding
+
+ /* For reference buf[5]=0 is none parity */
+ /* For reference buf[5]=1 is odd parity */
+ /* For reference buf[5]=2 is even parity */
+ /* For reference buf[5]=3 is mark parity */
+ /* For reference buf[5]=4 is space parity */
+ buf[5] = p_cdc->requested_line.coding.parity; // PL2303 has the same coding
+
+ return pl2303_set_request(p_cdc, PL2303_SET_LINE_REQUEST, PL2303_SET_LINE_REQUEST_TYPE, 0, 0,
+ buf, PL2303_LINE_CODING_BUFSIZE, complete_cb, user_data);
+}
+
+static bool pl2303_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // PL2303 has the same bit coding
+ return pl2303_set_request(p_cdc, PL2303_SET_CONTROL_REQUEST, PL2303_SET_CONTROL_REQUEST_TYPE,
+ p_cdc->requested_line.control_state.value, 0, NULL, 0, complete_cb, user_data);
+}
+
+//------------- Enumeration -------------//
+
+enum {
+ CONFIG_PL2303_DETECT_TYPE = 0,
+ CONFIG_PL2303_READ1,
+ CONFIG_PL2303_WRITE1,
+ CONFIG_PL2303_READ2,
+ CONFIG_PL2303_READ3,
+ CONFIG_PL2303_READ4,
+ CONFIG_PL2303_WRITE2,
+ CONFIG_PL2303_READ5,
+ CONFIG_PL2303_READ6,
+ CONFIG_PL2303_WRITE3,
+ CONFIG_PL2303_WRITE4,
+ CONFIG_PL2303_WRITE5,
+ CONFIG_PL2303_RESET_ENDP1,
+ CONFIG_PL2303_RESET_ENDP2,
+// CONFIG_PL2303_FLOW_CTRL_READ,
+// CONFIG_PL2303_FLOW_CTRL_WRITE,
+ CONFIG_PL2303_COMPLETE
+};
+
+static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ // PL2303 Interface includes 1 vendor interface + 1 interrupt endpoints + 2 bulk
+ TU_VERIFY(itf_desc->bNumEndpoints == 3);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t *p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ p_cdc->serial_drid = SERIAL_DRIVER_PL2303;
+ p_cdc->pl2303.quirks = 0;
+ p_cdc->pl2303.supports_hx_status = false;
+
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // Interrupt endpoint: not used for now
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
+ TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+ desc_ep += 1;
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+
+ return true;
+}
+
+static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+ // state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status()
+ const uintptr_t state = xfer->user_data;
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1);
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ pl2303_type_t type;
+
+ switch (state) {
+ // from here sequence overtaken from Linux Kernel function pl2303_startup()
+ case CONFIG_PL2303_DETECT_TYPE:
+ // get type and quirks (step 1)
+ type = pl2303_detect_type(p_cdc, 1);
+ TU_ASSERT(type != PL2303_TYPE_UNKNOWN);
+ if (type == PL2303_TYPE_NEED_SUPPORTS_HX_STATUS) {
+ TU_ASSERT(pl2303_supports_hx_status(p_cdc, cdch_process_set_config, CONFIG_PL2303_READ1));
+ break;
+ }
+ // no transfer triggered and continue with CONFIG_PL2303_READ1
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ1:
+ // get supports_hx_status, type and quirks (step 2), do special read
+ // will not be true, if coming directly from previous case
+ if (xfer->user_data == CONFIG_PL2303_READ1 && xfer->result == XFER_RESULT_SUCCESS) {
+ p_cdc->pl2303.supports_hx_status = true;
+ }
+ type = pl2303_detect_type(p_cdc, 2); // step 2 now with supports_hx_status
+ TU_ASSERT(type != PL2303_TYPE_UNKNOWN);
+ TU_LOG_DRV(" PL2303 type detected: %u\r\n", type);
+
+ p_cdc->pl2303.type = type;
+ p_cdc->pl2303.quirks |= pl2303_type_data[type].quirks;
+
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE1));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_WRITE1:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 0, cdch_process_set_config, CONFIG_PL2303_READ2));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ2:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ3));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ3:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ4));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ4:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE2));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_WRITE2:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 1, cdch_process_set_config, CONFIG_PL2303_READ5));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ5:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ6));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_READ6:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE3));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_WRITE3:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 0, 1, cdch_process_set_config, CONFIG_PL2303_WRITE4));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_WRITE4:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 1, 0, cdch_process_set_config, CONFIG_PL2303_WRITE5));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_PL2303_WRITE5:
+ // purpose unknown, overtaken from Linux Kernel driver
+ if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
+ uint16_t const windex = (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) ? 0x24 : 0x44;
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 2, windex, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP1));
+ break;
+ }// else: continue with next step
+ TU_ATTR_FALLTHROUGH;
+
+ // from here sequence overtaken from Linux Kernel function pl2303_open()
+ case CONFIG_PL2303_RESET_ENDP1:
+ // step 1
+ if (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) {
+ TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_OUT_EP, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP2));
+ } else {
+ /* reset upstream data pipes */
+ if (p_cdc->pl2303.type == PL2303_TYPE_HXN) {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_RESET_REG,// skip CONFIG_PL2303_RESET_ENDP2, no 2nd step
+ PL2303_HXN_RESET_UPSTREAM_PIPE | PL2303_HXN_RESET_DOWNSTREAM_PIPE,
+ cdch_process_set_config, CONFIG_PL2303_COMPLETE));
+ } else {
+ pl2303_vendor_write(p_cdc, 8, 0, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP2);
+ }
+ }
+ break;
+
+ case CONFIG_PL2303_RESET_ENDP2:
+ // step 2
+ if (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) {
+ TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_IN_EP, cdch_process_set_config, CONFIG_PL2303_COMPLETE));
+ } else {
+ /* reset upstream data pipes */
+ if (p_cdc->pl2303.type == PL2303_TYPE_HXN) {
+ // here nothing to do, only structure of previous step overtaken for better reading and comparison
+ } else {
+ TU_ASSERT(pl2303_vendor_write(p_cdc, 9, 0, cdch_process_set_config, CONFIG_PL2303_COMPLETE));
+ }
+ }
+ break;
+
+ // skipped, because it's not working with each PL230x. flow control can be also set by PL2303 EEPROM Writer Program
+ // case CONFIG_PL2303_FLOW_CTRL_READ:
+ // // read flow control register for modify & write back in next step
+ // if (p_cdc->pl2303.type == PL2303_TYPE_HXN) {
+ // TU_LOG_P_CDC ( "1\r\n" );
+ // TU_ASSERT(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf,
+ // cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE));
+ // } else {
+ // TU_LOG_P_CDC ( "2\r\n" );
+ // TU_ASSERT(pl2303_vendor_read(p_cdc, 0, &buf, cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE));
+ // }
+ // break;
+ //
+ // case CONFIG_PL2303_FLOW_CTRL_WRITE:
+ // // no flow control
+ // buf = xfer->buffer[0];
+ // if (p_cdc->pl2303.type == PL2303_TYPE_HXN) {
+ // buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK;
+ // buf |= PL2303_HXN_FLOWCTRL_NONE;
+ // TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf,
+ // cdch_process_set_config, CONFIG_PL2303_COMPLETE));
+ // } else {
+ // buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK;
+ // TU_ASSERT(pl2303_vendor_write(p_cdc, 0, buf,
+ // cdch_process_set_config, CONFIG_PL2303_COMPLETE));
+ // }
+ // break;
+
+ case CONFIG_PL2303_COMPLETE:
+ xfer->user_data = 0; // kick-off set line state on enum
+ cdch_process_line_state_on_enum(xfer);
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+//------------- Helper -------------//
+
+static pl2303_type_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) {
+ tusb_desc_device_t desc_dev;
+ TU_VERIFY(tuh_descriptor_get_device_local(p_cdc->daddr, &desc_dev), PL2303_TYPE_UNKNOWN);
+
+ // Legacy PL2303H, variants 0 and 1 (difference unknown).
+ if (desc_dev.bDeviceClass == 0x02) {
+ return PL2303_TYPE_H; /* variant 0 */
+ }
+
+ if (desc_dev.bMaxPacketSize0 != 0x40) {
+ if (desc_dev.bDeviceClass == 0x00 || desc_dev.bDeviceClass == 0xff) {
+ return PL2303_TYPE_H; /* variant 1 */
+ }
+ return PL2303_TYPE_H; /* variant 0 */
+ }
+
+ switch (desc_dev.bcdUSB) {
+ case 0x101:
+ /* USB 1.0.1? Let's assume they meant 1.1... */
+ TU_ATTR_FALLTHROUGH;
+ case 0x110:
+ switch (desc_dev.bcdDevice) {
+ case 0x300: return PL2303_TYPE_HX;
+ case 0x400: return PL2303_TYPE_HXD;
+ default: return PL2303_TYPE_HX;
+ }
+ break;
+
+ case 0x200:
+ switch (desc_dev.bcdDevice) {
+ case 0x100: /* GC */
+ case 0x105:
+ return PL2303_TYPE_HXN;
+
+ case 0x300: /* GT / TA */
+ if (step == 1) {
+ // step 1 trigger pl2303_supports_hx_status() request
+ return PL2303_TYPE_NEED_SUPPORTS_HX_STATUS;
+ } else {
+ // step 2 use supports_hx_status
+ if (p_cdc->pl2303.supports_hx_status) {
+ return PL2303_TYPE_TA;
+ }
+ }
+ TU_ATTR_FALLTHROUGH;
+ case 0x305:
+ case 0x400: /* GL */
+ case 0x405:
+ return PL2303_TYPE_HXN;
+
+ case 0x500: /* GE / TB */
+ if (step == 1) {
+ // step 1 trigger pl2303_supports_hx_status() request
+ return PL2303_TYPE_NEED_SUPPORTS_HX_STATUS;
+ } else {
+ // step 2 use supports_hx_status
+ if (p_cdc->pl2303.supports_hx_status) {
+ return PL2303_TYPE_TB;
+ }
+ }
+ TU_ATTR_FALLTHROUGH;
+ case 0x505:
+ case 0x600: /* GS */
+ case 0x605:
+ case 0x700: /* GR */
+ case 0x705:
+ return PL2303_TYPE_HXN;
+
+ default:
+ break; // unknown device
+ }
+ break;
+
+ default:
+ break; // unknown device
+ }
+
+ TU_LOG_CDC(p_cdc, "unknown device type bcdUSB = 0x%04x", desc_dev.bcdUSB);
+ return PL2303_TYPE_UNKNOWN;
+}
+
+/*
+ * Returns the nearest supported baud rate that can be set directly without
+ * using divisors.
+ */
+static uint32_t pl2303_get_supported_baud_rate(uint32_t baud) {
+ static const uint32_t baud_sup[] = {
+ 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
+ 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
+ 614400, 921600, 1228800, 2457600, 3000000, 6000000
+ };
+
+ uint8_t i;
+ for (i = 0; i < TU_ARRAY_SIZE(baud_sup); ++i) {
+ if (baud_sup[i] > baud) {
+ break;
+ }
+ }
+
+ if (i == TU_ARRAY_SIZE(baud_sup)) {
+ baud = baud_sup[i - 1];
+ } else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) {
+ baud = baud_sup[i - 1];
+ } else {
+ baud = baud_sup[i];
+ }
+
+ return baud;
+}
+
+/*
+ * NOTE: If unsupported baud rates are set directly, the PL2303 seems to
+ * use 9600 baud.
+ */
+static uint32_t pl2303_encode_baud_rate_direct(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
+ uint32_t baud_le = tu_htole32(baud);
+ buf[0] = (uint8_t) ( baud_le & 0xff);
+ buf[1] = (uint8_t) ((baud_le >> 8) & 0xff);
+ buf[2] = (uint8_t) ((baud_le >> 16) & 0xff);
+ buf[3] = (uint8_t) ((baud_le >> 24) & 0xff);
+
+ return baud;
+}
+
+static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
+ uint32_t baseline, mantissa, exponent;
+
+ /*
+ * Apparently the formula is:
+ * baudrate = 12M * 32 / (mantissa * 4^exponent)
+ * where
+ * mantissa = buf[8:0]
+ * exponent = buf[11:9]
+ */
+ baseline = 12000000 * 32;
+ mantissa = baseline / baud;
+ if (mantissa == 0) {
+ mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */
+ }
+ exponent = 0;
+ while (mantissa >= 512) {
+ if (exponent < 7) {
+ mantissa >>= 2; /* divide by 4 */
+ exponent++;
+ } else {
+ /* Exponent is maxed. Trim mantissa and leave. */
+ mantissa = 511;
+ break;
+ }
+ }
+
+ buf[3] = 0x80;
+ buf[2] = 0;
+ buf[1] = (uint8_t) ((exponent << 1 | mantissa >> 8) & 0xff);
+ buf[0] = (uint8_t) (mantissa & 0xff);
+
+ /* Calculate and return the exact baud rate. */
+ baud = (baseline / mantissa) >> (exponent << 1);
+
+ return baud;
+}
+
+static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
+ uint32_t baseline, mantissa, exponent;
+
+ /*
+ * Apparently, for the TA version the formula is:
+ * baudrate = 12M * 32 / (mantissa * 2^exponent)
+ * where
+ * mantissa = buf[10:0]
+ * exponent = buf[15:13 16]
+ */
+ baseline = 12000000 * 32;
+ mantissa = baseline / baud;
+ if (mantissa == 0) {
+ mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */
+ }
+ exponent = 0;
+ while (mantissa >= 2048) {
+ if (exponent < 15) {
+ mantissa >>= 1; /* divide by 2 */
+ exponent++;
+ } else {
+ /* Exponent is maxed. Trim mantissa and leave. */
+ mantissa = 2047;
+ break;
+ }
+ }
+
+ buf[3] = 0x80;
+ buf[2] = (uint8_t) (exponent & 0x01);
+ buf[1] = (uint8_t) (((exponent & (uint32_t) ~0x01) << 4 | mantissa >> 8) & 0xff);
+ buf[0] = (uint8_t) (mantissa & 0xff);
+
+ /* Calculate and return the exact baud rate. */
+ baud = (baseline / mantissa) >> exponent;
+
+ return baud;
+}
+
+static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]) {
+ uint32_t baud = p_cdc->requested_line.coding.bit_rate;
+ uint32_t baud_sup;
+ const pl2303_type_data_t* type_data = &pl2303_type_data[p_cdc->pl2303.type];
+
+ TU_VERIFY(baud && baud <= type_data->max_baud_rate);
+ /*
+ * Use direct method for supported baud rates, otherwise use divisors.
+ * Newer chip types do not support divisor encoding.
+ */
+ if (type_data->no_divisors != 0) {
+ baud_sup = baud;
+ } else {
+ baud_sup = pl2303_get_supported_baud_rate(baud);
+ }
+
+ if (baud == baud_sup) {
+ baud = pl2303_encode_baud_rate_direct(buf, baud);
+ } else if (type_data->alt_divisors != 0) {
+ baud = pl2303_encode_baud_rate_divisor_alt(buf, baud);
+ } else {
+ baud = pl2303_encode_baud_rate_divisor(buf, baud);
+ }
+ TU_LOG_CDC(p_cdc, "real baudrate %lu", baud);
+
+ return true;
+}
+
+#endif // CFG_TUH_CDC_PL2303
+
#endif
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 8c5399534..e8637beac 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_CDC_HOST_H_
-#define _TUSB_CDC_HOST_H_
+#ifndef TUSB_CDC_HOST_H_
+#define TUSB_CDC_HOST_H_
#include "cdc.h"
@@ -37,16 +37,6 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
-// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1)
-#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
-#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0
-#endif
-
-// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
-//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
-//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
-//#endif
-
// RX FIFO size
#ifndef CFG_TUH_CDC_RX_BUFSIZE
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
@@ -79,14 +69,27 @@ uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
// 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 a interface is mounted
+// 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
-bool tuh_cdc_get_dtr(uint8_t idx);
+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
-bool tuh_cdc_get_rts(uint8_t idx);
+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) {
@@ -97,7 +100,9 @@ TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
// NOTE: This function does not make any USB transfer request to device.
-bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding);
+bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t* line_coding);
+
+#define tuh_cdc_get_local_line_coding tuh_cdc_get_line_coding_local // backward compatibility
//--------------------------------------------------------------------+
// Write API
@@ -132,18 +137,31 @@ bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
bool tuh_cdc_read_clear (uint8_t idx);
//--------------------------------------------------------------------+
-// Control Endpoint (Request) API
+// 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 pointed to xfer_result_t to hold the transfer result.
-// - The function will return true if transfer is successful, false otherwise.
+// 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);
@@ -160,33 +178,68 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding,
// 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) {
+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) {
+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.
-TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx);
+extern void tuh_cdc_mount_cb(uint8_t idx);
// Invoked when a device with CDC interface is unmounted
-TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx);
+extern void tuh_cdc_umount_cb(uint8_t idx);
// Invoked when received new data
-TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx);
+extern void tuh_cdc_rx_cb(uint8_t idx);
// Invoked when a TX is complete and therefore space becomes available in TX buffer
-TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx);
+extern void tuh_cdc_tx_complete_cb(uint8_t idx);
//--------------------------------------------------------------------+
// Internal Class Driver API
@@ -202,4 +255,4 @@ void cdch_close (uint8_t dev_addr);
}
#endif
-#endif /* _TUSB_CDC_HOST_H_ */
+#endif /* TUSB_CDC_HOST_H_ */
diff --git a/src/class/cdc/cdc_rndis.h b/src/class/cdc/cdc_rndis.h
index ad153e0ac..fbbd43206 100644
--- a/src/class/cdc/cdc_rndis.h
+++ b/src/class/cdc/cdc_rndis.h
@@ -30,8 +30,8 @@
* \defgroup CDC_RNDIS_Common Common Definitions
* @{ */
-#ifndef _TUSB_CDC_RNDIS_H_
-#define _TUSB_CDC_RNDIS_H_
+#ifndef TUSB_CDC_RNDIS_H_
+#define TUSB_CDC_RNDIS_H_
#include "cdc.h"
@@ -295,7 +295,7 @@ typedef enum
}
#endif
-#endif /* _TUSB_CDC_RNDIS_H_ */
+#endif /* TUSB_CDC_RNDIS_H_ */
/** @} */
/** @} */
diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c
index 11a5355aa..e975ea440 100644
--- a/src/class/cdc/cdc_rndis_host.c
+++ b/src/class/cdc/cdc_rndis_host.c
@@ -103,7 +103,7 @@ static tusb_error_t rndis_body_subtask(void)
}
- osal_task_delay(100);
+ tusb_time_delay_ms_api(100);
OSAL_SUBTASK_END
}
diff --git a/src/class/cdc/cdc_rndis_host.h b/src/class/cdc/cdc_rndis_host.h
index bb431ec1f..e70d27f79 100644
--- a/src/class/cdc/cdc_rndis_host.h
+++ b/src/class/cdc/cdc_rndis_host.h
@@ -28,8 +28,8 @@
* \defgroup CDC_RNSID_Host Host
* @{ */
-#ifndef _TUSB_CDC_RNDIS_HOST_H_
-#define _TUSB_CDC_RNDIS_HOST_H_
+#ifndef TUSB_CDC_RNDIS_HOST_H_
+#define TUSB_CDC_RNDIS_HOST_H_
#include "common/tusb_common.h"
#include "host/usbh.h"
@@ -58,6 +58,6 @@ void rndish_close(uint8_t dev_addr);
}
#endif
-#endif /* _TUSB_CDC_RNDIS_HOST_H_ */
+#endif /* TUSB_CDC_RNDIS_HOST_H_ */
/** @} */
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
index c18066f57..0e08b0acd 100644
--- a/src/class/cdc/serial/ch34x.h
+++ b/src/class/cdc/serial/ch34x.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _CH34X_H_
-#define _CH34X_H_
+#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
@@ -34,51 +34,51 @@
// 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
+ #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 }
+ #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
+#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 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 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)
+#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 )
+#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
+#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 /* _CH34X_H_ */
+#endif // TUSB_CH34X_H
diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h
index 2c749f522..a0eff9e40 100644
--- a/src/class/cdc/serial/cp210x.h
+++ b/src/class/cdc/serial/cp210x.h
@@ -28,9 +28,10 @@
// Protocol details can be found at AN571: CP210x Virtual COM Port Interface
// https://www.silabs.com/documents/public/application-notes/AN571.pdf
-#define TU_CP210X_VID 0x10C4
+// parts are overtaken from vendors driver
+// https://www.silabs.com/documents/public/software/cp210x-3.1.0.tar.gz
-/* Config request codes */
+// Config request codes
#define CP210X_IFC_ENABLE 0x00
#define CP210X_SET_BAUDDIV 0x01
#define CP210X_GET_BAUDDIV 0x02
@@ -59,4 +60,55 @@
#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
diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h
index 0825f0719..9bd56cef4 100644
--- a/src/class/cdc/serial/ftdi_sio.h
+++ b/src/class/cdc/serial/ftdi_sio.h
@@ -25,222 +25,194 @@
#ifndef TUSB_FTDI_SIO_H
#define TUSB_FTDI_SIO_H
-// VID for matching FTDI devices
-#define TU_FTDI_VID 0x0403
+#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 0x0a /* Get the latency timer */
-#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
-#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
-#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
+#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
-/* FTDI_SIO_RESET */
-#define FTDI_SIO_RESET_SIO 0
-#define FTDI_SIO_RESET_PURGE_RX 1
-#define FTDI_SIO_RESET_PURGE_TX 2
+// Channel indices for FT2232, FT2232H and FT4232H devices
+#define CHANNEL_A 1
+#define CHANNEL_B 2
+#define CHANNEL_C 3
+#define CHANNEL_D 4
-/*
- * BmRequestType: 0100 0000B
- * bRequest: FTDI_SIO_RESET
- * wValue: Control Value
- * 0 = Reset SIO
- * 1 = Purge RX buffer
- * 2 = Purge TX buffer
- * wIndex: Port
- * wLength: 0
- * Data: None
- *
- * The Reset SIO command has this effect:
- *
- * Sets flow control set to 'none'
- * Event char = $0D
- * Event trigger = disabled
- * Purge RX buffer
- * Purge TX buffer
- * Clear DTR
- * Clear RTS
- * baud and data format not reset
- *
- * The Purge RX and TX buffer commands affect nothing except the buffers
- *
- */
+// 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_MODEM_CTRL */
-/*
- * BmRequestType: 0100 0000B
- * bRequest: FTDI_SIO_MODEM_CTRL
- * wValue: ControlValue (see below)
- * wIndex: Port
- * wLength: 0
- * Data: None
- *
- * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
- * command will be IGNORED without an error being returned
- * Also - you can not set DTR and RTS with one control message
- */
+// 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
-#define FTDI_SIO_SET_DTR_MASK 0x1
-#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1)
-#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0)
-#define FTDI_SIO_SET_RTS_MASK 0x2
-#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2)
-#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0)
+// FTDI_SIO_SET_BAUDRATE
+#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
-/*
- * ControlValue
- * B0 DTR state
- * 0 = reset
- * 1 = set
- * B1 RTS state
- * 0 = reset
- * 1 = set
- * B2..7 Reserved
- * B8 DTR state enable
- * 0 = ignore
- * 1 = use DTR state
- * B9 RTS state enable
- * 0 = ignore
- * 1 = use RTS state
- * B10..15 Reserved
- */
+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_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_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)
-/*
- * BmRequestType: 0100 0000b
- * bRequest: FTDI_SIO_SET_FLOW_CTRL
- * wValue: Xoff/Xon
- * wIndex: Protocol/Port - hIndex is protocol / lIndex is port
- * wLength: 0
- * Data: None
- *
- * hIndex protocol is:
- * B0 Output handshaking using RTS/CTS
- * 0 = disabled
- * 1 = enabled
- * B1 Output handshaking using DTR/DSR
- * 0 = disabled
- * 1 = enabled
- * B2 Xon/Xoff handshaking
- * 0 = disabled
- * 1 = enabled
- *
- * A value of zero in the hIndex field disables handshaking
- *
- * If Xon/Xoff handshaking is specified, the hValue field should contain the
- * XOFF character and the lValue field contains the XON character.
- */
+// FTDI_SIO_MODEM_CTRL
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL
-/* FTDI_SIO_SET_BAUD_RATE */
-/*
- * BmRequestType: 0100 0000B
- * bRequest: FTDI_SIO_SET_BAUDRATE
- * wValue: BaudDivisor value - see below
- * wIndex: Port
- * wLength: 0
- * Data: None
- * The BaudDivisor values are calculated as follows (too complicated):
- */
+#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_DATA */
-#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)
-#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
-#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
-#define FTDI_SIO_SET_BREAK (0x1 << 14)
+// 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)
-/*
- * BmRequestType: 0100 0000B
- * bRequest: FTDI_SIO_SET_DATA
- * wValue: Data characteristics (see below)
- * wIndex: Port
- * wLength: 0
- * Data: No
- *
- * Data characteristics
- *
- * B0..7 Number of data bits
- * B8..10 Parity
- * 0 = None
- * 1 = Odd
- * 2 = Even
- * 3 = Mark
- * 4 = Space
- * B11..13 Stop Bits
- * 0 = 1
- * 1 = 1.5
- * 2 = 2
- * B14
- * 1 = TX ON (break)
- * 0 = TX OFF (normal state)
- * B15 Reserved
- *
- */
+// 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
-/*
-* DATA FORMAT
-*
-* IN Endpoint
-*
-* The device reserves the first two bytes of data on this endpoint to contain
-* the current values of the modem and line status registers. In the absence of
-* data, the device generates a message consisting of these two status bytes
- * every 40 ms
- *
- * Byte 0: Modem Status
-*
-* Offset Description
-* B0 Reserved - must be 1
-* B1 Reserved - must be 0
-* B2 Reserved - must be 0
-* B3 Reserved - must be 0
-* B4 Clear to Send (CTS)
-* B5 Data Set Ready (DSR)
-* B6 Ring Indicator (RI)
-* B7 Receive Line Signal Detect (RLSD)
-*
-* Byte 1: Line Status
-*
-* Offset Description
-* B0 Data Ready (DR)
-* B1 Overrun Error (OE)
-* B2 Parity Error (PE)
-* B3 Framing Error (FE)
-* B4 Break Interrupt (BI)
-* B5 Transmitter Holding Register (THRE)
-* B6 Transmitter Empty (TEMT)
-* B7 Error in RCVR FIFO
-*
-*/
-#define FTDI_RS0_CTS (1 << 4)
-#define FTDI_RS0_DSR (1 << 5)
-#define FTDI_RS0_RI (1 << 6)
-#define FTDI_RS0_RLSD (1 << 7)
+// 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_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)
+#define FTDI_OK true
+#define FTDI_FAIL false
+#define FTDI_NOT_POSSIBLE -1
+#define FTDI_REQUESTED -2
#endif //TUSB_FTDI_SIO_H
diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h
new file mode 100644
index 000000000..63910c7bb
--- /dev/null
+++ b/src/class/cdc/serial/pl2303.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
diff --git a/src/class/dfu/dfu.h b/src/class/dfu/dfu.h
index 114c827b8..8cd63656a 100644
--- a/src/class/dfu/dfu.h
+++ b/src/class/dfu/dfu.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_H_
-#define _TUSB_DFU_H_
+#ifndef TUSB_DFU_H_
+#define TUSB_DFU_H_
#include "common/tusb_common.h"
@@ -116,4 +116,4 @@ TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
}
#endif
-#endif /* _TUSB_DFU_H_ */
+#endif /* TUSB_DFU_H_ */
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index d9e2d3f2f..0d2b63b57 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -77,6 +77,24 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus
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
@@ -234,9 +252,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
- if (tud_dfu_detach_cb) {
- tud_dfu_detach_cb();
- }
+ tud_dfu_detach_cb();
}
break;
@@ -258,16 +274,13 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
reset_state();
tud_control_status(rhport, request);
} else if (stage == CONTROL_STAGE_ACK) {
- if (tud_dfu_abort_cb) {
- tud_dfu_abort_cb(_dfu_ctx.alt);
- }
+ tud_dfu_abort_cb(_dfu_ctx.alt);
}
break;
case DFU_REQUEST_UPLOAD:
if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
- TU_VERIFY(tud_dfu_upload_cb);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h
index 00c22ea8b..b22b4c450 100644
--- a/src/class/dfu/dfu_device.h
+++ b/src/class/dfu/dfu_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_DEVICE_H_
-#define _TUSB_DFU_DEVICE_H_
+#ifndef TUSB_DFU_DEVICE_H_
+#define TUSB_DFU_DEVICE_H_
#include "dfu.h"
@@ -74,13 +74,13 @@ void tud_dfu_manifest_cb(uint8_t alt);
// Invoked when received DFU_UPLOAD request
// Application must populate data with up to length bytes and
// Return the number of written bytes
-TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
+uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
// Invoked when a DFU_DETACH request is received
-TU_ATTR_WEAK void tud_dfu_detach_cb(void);
+void tud_dfu_detach_cb(void);
// Invoked when the Host has terminated a download or upload transfer
-TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
+void tud_dfu_abort_cb(uint8_t alt);
//--------------------------------------------------------------------+
// Internal Class Driver API
@@ -96,4 +96,4 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_r
}
#endif
-#endif /* _TUSB_DFU_MODE_DEVICE_H_ */
+#endif /* TUSB_DFU_MODE_DEVICE_H_ */
diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h
index 67eb26d95..c4116d8fe 100644
--- a/src/class/dfu/dfu_rt_device.h
+++ b/src/class/dfu/dfu_rt_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DFU_RT_DEVICE_H_
-#define _TUSB_DFU_RT_DEVICE_H_
+#ifndef TUSB_DFU_RT_DEVICE_H_
+#define TUSB_DFU_RT_DEVICE_H_
#include "dfu.h"
@@ -52,4 +52,4 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_req
}
#endif
-#endif /* _TUSB_DFU_RT_DEVICE_H_ */
+#endif /* TUSB_DFU_RT_DEVICE_H_ */
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index c2434c20d..0883d95ac 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -28,8 +28,8 @@
* \defgroup ClassDriver_HID Human Interface Device (HID)
* @{ */
-#ifndef _TUSB_HID_H_
-#define _TUSB_HID_H_
+#ifndef TUSB_HID_H_
+#define TUSB_HID_H_
#include "common/tusb_common.h"
@@ -807,7 +807,9 @@ enum {
HID_USAGE_PAGE_MEDICAL_INSTRUMENT = 0x40,
HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59,
HID_USAGE_PAGE_MONITOR = 0x80, // 0x80 - 0x83
- HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
+ HID_USAGE_PAGE_POWER = 0x84,
+ HID_USAGE_PAGE_BATTERY = 0x85,
+ // 0x86 - 0x87 is reserved for Power Device
HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
HID_USAGE_PAGE_SCALE = 0x8d,
HID_USAGE_PAGE_MSR = 0x8e,
@@ -893,14 +895,51 @@ enum {
/// HID Usage Table: Consumer Page (0x0C)
/// Only contains controls that supported by Windows (whole list is too long)
enum {
+ HID_USAGE_CONSUMER_UNASSIGNED = 0x0000,
+
// Generic Control
HID_USAGE_CONSUMER_CONTROL = 0x0001,
+ HID_USAGE_CONSUMER_NUMERIC_KEY_PAD = 0x0002,
+ HID_USAGE_CONSUMER_PROGRAMMABLE_BUTTONS = 0x0003,
+ HID_USAGE_CONSUMER_MICROPHONE = 0x0004,
+ HID_USAGE_CONSUMER_HEADPHONE = 0x0005,
+ HID_USAGE_CONSUMER_GRAPHIC_EQUALIZER = 0x0006,
+ // 07-1F Reserved
+
+ HID_USAGE_CONSUMER_PLUS_10 = 0x0020,
+ HID_USAGE_CONSUMER_PLUS_100 = 0x0021,
+ HID_USAGE_CONSUMER_AM_PM = 0x0022,
+ // 23-3F Reserved
// Power Control
HID_USAGE_CONSUMER_POWER = 0x0030,
HID_USAGE_CONSUMER_RESET = 0x0031,
HID_USAGE_CONSUMER_SLEEP = 0x0032,
+ HID_USAGE_CONSUMER_SLEEP_AFTER = 0x0033,
+ HID_USAGE_CONSUMER_SLEEP_MODE = 0x0034,
+ HID_USAGE_CONSUMER_ILLUMINATION = 0x0035,
+ HID_USAGE_CONSUMER_FUNCTION_BUTTONS = 0x0036,
+ // 37-3F Reserved
+ HID_USAGE_CONSUMER_MENU = 0x0040,
+ HID_USAGE_CONSUMER_MENU_PICK = 0x0041,
+ HID_USAGE_CONSUMER_MENU_UP = 0x0042,
+ HID_USAGE_CONSUMER_MENU_DOWN = 0x0043,
+ HID_USAGE_CONSUMER_MENU_LEFT = 0x0044,
+ HID_USAGE_CONSUMER_MENU_RIGHT = 0x0045,
+ HID_USAGE_CONSUMER_MENU_ESCAPE = 0x0046,
+ HID_USAGE_CONSUMER_MENU_VALUE_INCREASE = 0x0047,
+ HID_USAGE_CONSUMER_MENU_VALUE_DECREASE = 0x0048,
+ // 49-5F Reserved
+ HID_USAGE_CONSUMER_DATA_ON_SCREEN = 0x0060,
+ HID_USAGE_CONSUMER_CLOSED_CAPTION = 0x0061,
+ HID_USAGE_CONSUMER_CLOSED_CAPTION_SELECT = 0x0062,
+ HID_USAGE_CONSUMER_VCR_TV = 0x0063,
+ HID_USAGE_CONSUMER_BROADCAST_MODE = 0x0064,
+ HID_USAGE_CONSUMER_SNAPSHOT = 0x0065,
+ HID_USAGE_CONSUMER_STILL = 0x0066,
+
+ // 67-7F Reserved
// Screen Brightness
HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F,
HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070,
@@ -912,40 +951,382 @@ enum {
HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7,
HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8,
+ HID_USAGE_CONSUMER_SELECTION = 0x0080,
+ HID_USAGE_CONSUMER_ASSIGN_SELECTION = 0x0081,
+ HID_USAGE_CONSUMER_MODE_STEP = 0x0082,
+ HID_USAGE_CONSUMER_RECALL_LAST = 0x0083,
+ HID_USAGE_CONSUMER_ENTER_CHANNEL = 0x0084,
+ HID_USAGE_CONSUMER_ORDER_MOVIE = 0x0085,
+ HID_USAGE_CONSUMER_CHANNEL = 0x0086,
+ HID_USAGE_CONSUMER_MEDIA_SELECTION = 0x0087,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_COMPUTER = 0x0088,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_TV = 0x0089,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_WWW = 0x008A,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_DVD = 0x008B,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_TELEPHONE = 0x008C,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE = 0x008D,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_VIDEO_PHONE = 0x008E,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_GAMES = 0x008F,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_MESSAGES = 0x0090,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_CD = 0x0091,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_VCR = 0x0092,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_TUNER = 0x0093,
+ HID_USAGE_CONSUMER_QUIT = 0x0094,
+ HID_USAGE_CONSUMER_HELP = 0x0095,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_TAPE = 0x0096,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_CABLE = 0x0097,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_SATELLITE = 0x0098,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_SECURITY = 0x0099,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_HOME = 0x009A,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_CALL = 0x009B,
+ HID_USAGE_CONSUMER_CHANNEL_INCREMENT = 0x009C,
+ HID_USAGE_CONSUMER_CHANNEL_DECREMENT = 0x009D,
+ HID_USAGE_CONSUMER_MEDIA_SELECT_SAP = 0x009E,
+ // 9F Reserved
+ HID_USAGE_CONSUMER_VCR_PLUS = 0x00A0,
+ HID_USAGE_CONSUMER_ONCE = 0x00A1,
+ HID_USAGE_CONSUMER_DAILY = 0x00A2,
+ HID_USAGE_CONSUMER_WEEKLY = 0x00A3,
+ HID_USAGE_CONSUMER_MONTHLY = 0x00A4,
+ // A5-AF Reserved
+
+ HID_USAGE_CONSUMER_PLAY = 0x00B0,
+ HID_USAGE_CONSUMER_PAUSE = 0x00B1,
+ HID_USAGE_CONSUMER_RECORD = 0x00B2,
+ HID_USAGE_CONSUMER_FAST_FORWARD = 0x00B3,
+ HID_USAGE_CONSUMER_REWIND = 0x00B4,
+ HID_USAGE_CONSUMER_SCAN_NEXT_TRACK = 0x00B5,
+ HID_USAGE_CONSUMER_SCAN_PREVIOUS_TRACK = 0x00B6,
+ HID_USAGE_CONSUMER_STOP = 0x00B7,
+ HID_USAGE_CONSUMER_EJECT = 0x00B8,
+ HID_USAGE_CONSUMER_RANDOM_PLAY = 0x00B9,
+ HID_USAGE_CONSUMER_SELECT_DISC = 0x00BA,
+ HID_USAGE_CONSUMER_ENTER_DISC = 0x00BB,
+ HID_USAGE_CONSUMER_REPEAT = 0x00BC,
+ HID_USAGE_CONSUMER_TRACKING = 0x00BD,
+ HID_USAGE_CONSUMER_TRACK_NORMAL = 0x00BE,
+ HID_USAGE_CONSUMER_SLOW_TRACKING = 0x00BF,
+ HID_USAGE_CONSUMER_FRAME_FORWARD = 0x00C0,
+ HID_USAGE_CONSUMER_FRAME_BACK = 0x00C1,
+ HID_USAGE_CONSUMER_MARK = 0x00C2,
+ HID_USAGE_CONSUMER_CLEAR_MARK = 0x00C3,
+ HID_USAGE_CONSUMER_REPEAT_FROM_MARK = 0x00C4,
+ HID_USAGE_CONSUMER_RETURN_TO_MARK = 0x00C5,
+ HID_USAGE_CONSUMER_SEARCH_MARK_FORWARD = 0x00C6,
+ HID_USAGE_CONSUMER_SEARCH_MARK_BACKWARDS = 0x00C7,
+ HID_USAGE_CONSUMER_COUNTER_RESET = 0x00C8,
+ HID_USAGE_CONSUMER_SHOW_COUNTER = 0x00C9,
+ HID_USAGE_CONSUMER_TRACKING_INCREMENT = 0x00CA,
+ HID_USAGE_CONSUMER_TRACKING_DECREMENT = 0x00CB,
+ HID_USAGE_CONSUMER_STOP_EJECT = 0x00CC,
+
+
// Media Control
HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD,
- HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5,
- HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6,
- HID_USAGE_CONSUMER_STOP = 0x00B7,
+
+ HID_USAGE_CONSUMER_PLAY_SKIP = 0x00CE,
+
+ // CF-DF Reserved
HID_USAGE_CONSUMER_VOLUME = 0x00E0,
+ HID_USAGE_CONSUMER_BALANCE = 0x00E1,
HID_USAGE_CONSUMER_MUTE = 0x00E2,
HID_USAGE_CONSUMER_BASS = 0x00E3,
HID_USAGE_CONSUMER_TREBLE = 0x00E4,
HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5,
+ HID_USAGE_CONSUMER_SURROUND_MODE = 0x00E6,
+ HID_USAGE_CONSUMER_LOUDNESS = 0x00E7,
+ HID_USAGE_CONSUMER_MPX = 0x00E8,
HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9,
HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA,
+ // EB-EF Reserved
+ HID_USAGE_CONSUMER_SPEED_SELECT = 0x00F0,
+ HID_USAGE_CONSUMER_PLAYBACK_SPEED = 0x00F1,
+ HID_USAGE_CONSUMER_STANDARD_PLAY = 0x00F2,
+ HID_USAGE_CONSUMER_LONG_PLAY = 0x00F3,
+ HID_USAGE_CONSUMER_EXTENDED_PLAY = 0x00F4,
+ HID_USAGE_CONSUMER_SLOW = 0x00F5,
+ // F6-FF Reserved
+ HID_USAGE_CONSUMER_FAN_ENABLE = 0x0100,
+ HID_USAGE_CONSUMER_FAN_SPEED = 0x0101,
+ HID_USAGE_CONSUMER_LIGHT_ENABLE = 0x0102,
+ HID_USAGE_CONSUMER_LIGHT_ILLUMINATION_LEVEL = 0x0103,
+ HID_USAGE_CONSUMER_CLIMATE_CONTROL_ENABLE = 0x0104,
+ HID_USAGE_CONSUMER_ROOM_TEMPERATURE = 0x0105,
+ HID_USAGE_CONSUMER_SECURITY_ENABLE = 0x0106,
+ HID_USAGE_CONSUMER_FIRE_ALARM = 0x0107,
+ HID_USAGE_CONSUMER_POLICE_ALARM = 0x0108,
+ HID_USAGE_CONSUMER_PROXIMITY = 0x0109,
+ HID_USAGE_CONSUMER_MOTION = 0x010A,
+ HID_USAGE_CONSUMER_DURESS_ALARM = 0x010B,
+ HID_USAGE_CONSUMER_HOLDUP_ALARM = 0x010C,
+ HID_USAGE_CONSUMER_MEDICAL_ALARM = 0x010D,
+ // 10E-14F Reserved
+ HID_USAGE_CONSUMER_BALANCE_RIGHT = 0x0150,
+ HID_USAGE_CONSUMER_BALANCE_LEFT = 0x0151,
HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152,
HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153,
HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154,
HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155,
- // Application Launcher
+ // 156-15F Reserved
+ HID_USAGE_CONSUMER_SPEAKER_SYSTEM = 0x0160,
+ HID_USAGE_CONSUMER_CHANNEL_LEFT = 0x0161,
+ HID_USAGE_CONSUMER_CHANNEL_RIGHT = 0x0162,
+ HID_USAGE_CONSUMER_CHANNEL_CENTER = 0x0163,
+ HID_USAGE_CONSUMER_CHANNEL_FRONT = 0x0164,
+ HID_USAGE_CONSUMER_CHANNEL_CENTER_FRONT = 0x0165,
+ HID_USAGE_CONSUMER_CHANNEL_SIDE = 0x0166,
+ HID_USAGE_CONSUMER_CHANNEL_SURROUND = 0x0167,
+ HID_USAGE_CONSUMER_CHANNEL_LOW_FREQUENCY = 0x0168,
+ // Enhancement
+ // CL 15.12.1
+ HID_USAGE_CONSUMER_CHANNEL_TOP = 0x0169,
+ HID_USAGE_CONSUMER_CHANNEL_UNKNOWN = 0x016A,
+ // 16B-16F Reserved
+ HID_USAGE_CONSUMER_SUB_CHANNEL = 0x0170,
+ HID_USAGE_CONSUMER_SUB_CHANNEL_INCREMENT = 0x0171,
+ HID_USAGE_CONSUMER_SUB_CHANNEL_DECREMENT = 0x0172,
+ HID_USAGE_CONSUMER_ALTERNATE_AUDIO_INCREMENT = 0x0173,
+ HID_USAGE_CONSUMER_ALTERNATE_AUDIO_DECREMENT = 0x0174,
+ // 175-17F Reserved
+ HID_USAGE_CONSUMER_APPLICATION_LAUNCH_BUTTONS = 0x0180,
+ HID_USAGE_CONSUMER_AL_LAUNCH_BUTTON_CONFIGURATION = 0x0181,
+ // Tool
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_PROGRAMMABLE_BUTTON = 0x0182,
+ // Configuration
+ // Sel 15.15
HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183,
+ // Configuration
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_WORD_PROCESSOR = 0x0184,
+ HID_USAGE_CONSUMER_AL_TEXT_EDITOR = 0x0185,
+ HID_USAGE_CONSUMER_AL_SPREADSHEET = 0x0186,
+ HID_USAGE_CONSUMER_AL_GRAPHICS_EDITOR = 0x0187,
+ HID_USAGE_CONSUMER_AL_PRESENTATION_APP = 0x0188,
+ HID_USAGE_CONSUMER_AL_DATABASE_APP = 0x0189,
HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A,
+ HID_USAGE_CONSUMER_AL_NEWSREADER = 0x018B,
+ HID_USAGE_CONSUMER_AL_VOICEMAIL = 0x018C,
+ HID_USAGE_CONSUMER_AL_CONTACTS_ADDRESS_BOOK = 0x018D,
+ HID_USAGE_CONSUMER_AL_CALENDAR_SCHEDULE = 0x018E,
+ HID_USAGE_CONSUMER_AL_TASK_PROJECT_MANAGER = 0x018F,
+ HID_USAGE_CONSUMER_AL_LOG_JOURNAL_TIMECARD = 0x0190,
+ HID_USAGE_CONSUMER_AL_CHECKBOOK_FINANCE = 0x0191,
HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192,
- HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194,
-
+ HID_USAGE_CONSUMER_AL_A_V_CAPTURE_PLAYBACK = 0x0193,
+ HID_USAGE_CONSUMER_AL_LOCAL_MACHINE_BROWSER = 0x0194,
+ HID_USAGE_CONSUMER_AL_LAN_WAN_BROWSER = 0x0195,
+ HID_USAGE_CONSUMER_AL_INTERNET_BROWSER = 0x0196,
+ HID_USAGE_CONSUMER_AL_REMOTE_NETWORKING_ISP = 0x0197,
+ // Connect
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_NETWORK_CONFERENCE = 0x0198,
+ HID_USAGE_CONSUMER_AL_NETWORK_CHAT = 0x0199,
+ HID_USAGE_CONSUMER_AL_TELEPHONY_DIALER = 0x019A,
+ HID_USAGE_CONSUMER_AL_LOGON = 0x019B,
+ HID_USAGE_CONSUMER_AL_LOGOFF = 0x019C,
+ HID_USAGE_CONSUMER_AL_LOGON_LOGOFF = 0x019D,
+ HID_USAGE_CONSUMER_AL_TERMINAL_LOCK_SCREENSAVER = 0x019E,
+ HID_USAGE_CONSUMER_AL_CONTROL_PANEL = 0x019F,
+ HID_USAGE_CONSUMER_AL_COMMAND_LINE_PROCESSOR_RUN = 0x01A0,
+ HID_USAGE_CONSUMER_AL_PROCESS_TASK_MANAGER = 0x01A1,
+ HID_USAGE_CONSUMER_AL_SELECT_TASK_APPLICATION = 0x01A2,
+ HID_USAGE_CONSUMER_AL_NEXT_TASK_APPLICATION = 0x01A3,
+ HID_USAGE_CONSUMER_AL_PREVIOUS_TASK_APPLICATION = 0x01A4,
+ HID_USAGE_CONSUMER_AL_PREEMPTIVE_HALT = 0x01A5,
+ // Task_Application
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_INTEGRATED_HELP_CENTER = 0x01A6,
+ HID_USAGE_CONSUMER_AL_DOCUMENTS = 0x01A7,
+ HID_USAGE_CONSUMER_AL_THESAURUS = 0x01A8,
+ HID_USAGE_CONSUMER_AL_DICTIONARY = 0x01A9,
+ HID_USAGE_CONSUMER_AL_DESKTOP = 0x01AA,
+ HID_USAGE_CONSUMER_AL_SPELL_CHECK = 0x01AB,
+ HID_USAGE_CONSUMER_AL_GRAMMAR_CHECK = 0x01AC,
+ HID_USAGE_CONSUMER_AL_WIRELESS_STATUS = 0x01AD,
+ HID_USAGE_CONSUMER_AL_KEYBOARD_LAYOUT = 0x01AE,
+ HID_USAGE_CONSUMER_AL_VIRUS_PROTECTION = 0x01AF,
+ HID_USAGE_CONSUMER_AL_ENCRYPTION = 0x01B0,
+ HID_USAGE_CONSUMER_AL_SCREEN_SAVER = 0x01B1,
+ HID_USAGE_CONSUMER_AL_ALARMS = 0x01B2,
+ HID_USAGE_CONSUMER_AL_CLOCK = 0x01B3,
+ HID_USAGE_CONSUMER_AL_FILE_BROWSER = 0x01B4,
+ HID_USAGE_CONSUMER_AL_POWER_STATUS = 0x01B5,
+ HID_USAGE_CONSUMER_AL_IMAGE_BROWSER = 0x01B6,
+ HID_USAGE_CONSUMER_AL_AUDIO_BROWSER = 0x01B7,
+ HID_USAGE_CONSUMER_AL_MOVIE_BROWSER = 0x01B8,
+ HID_USAGE_CONSUMER_AL_DIGITAL_RIGHTS_MANAGER = 0x01B9,
+ HID_USAGE_CONSUMER_AL_DIGITAL_WALLET = 0x01BA,
+ // 1BB Reserved
+ HID_USAGE_CONSUMER_AL_INSTANT_MESSAGING = 0x01BC,
+ HID_USAGE_CONSUMER_AL_OEM_FEATURES_TIPS_TUTORIAL = 0x01BD,
+ // Browser
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_OEM_HELP = 0x01BE,
+ HID_USAGE_CONSUMER_AL_ONLINE_COMMUNITY = 0x01BF,
+ HID_USAGE_CONSUMER_AL_ENTERTAINMENT_CONTENT = 0x01C0,
+ // Browser
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_ONLINE_SHOPPING_BROWSER = 0x01C1,
+ HID_USAGE_CONSUMER_AL_SMARTCARD_INFORMATION_HELP = 0x01C2,
+ HID_USAGE_CONSUMER_AL_MARKET_MONITOR_FINANCE = 0x01C3,
+ // Browser
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_CUSTOMIZED_CORPORATE_NEWS = 0x01C4,
+ // Browser
+ // Sel 15.15
+ HID_USAGE_CONSUMER_AL_ONLINE_ACTIVITY_BROWSER = 0x01C5,
+ HID_USAGE_CONSUMER_AL_RESEARCH_SEARCH_BROWSER = 0x01C6,
+ HID_USAGE_CONSUMER_AL_AUDIO_PLAYER = 0x01C7,
+ // 1C8-1FF Reserved
+ HID_USAGE_CONSUMER_GENERIC_GUI_APPLICATION = 0x0200,
+ // ' Controls
+ // '
+ HID_USAGE_CONSUMER_AC_NEW = 0x0201,
+ HID_USAGE_CONSUMER_AC_OPEN = 0x0202,
+ HID_USAGE_CONSUMER_AC_CLOSE = 0x0203,
+ HID_USAGE_CONSUMER_AC_EXIT = 0x0204,
+ HID_USAGE_CONSUMER_AC_MAXIMIZE = 0x0205,
+ HID_USAGE_CONSUMER_AC_MINIMIZE = 0x0206,
+ HID_USAGE_CONSUMER_AC_SAVE = 0x0207,
+ HID_USAGE_CONSUMER_AC_PRINT = 0x0208,
+ HID_USAGE_CONSUMER_AC_PROPERTIES = 0x0209,
+ HID_USAGE_CONSUMER_AC_UNDO = 0x021A,
+ HID_USAGE_CONSUMER_AC_COPY = 0x021B,
+ HID_USAGE_CONSUMER_AC_CUT = 0x021C,
+ HID_USAGE_CONSUMER_AC_PASTE = 0x021D,
+ HID_USAGE_CONSUMER_AC_SELECT_ALL = 0x021E,
+ HID_USAGE_CONSUMER_AC_FIND = 0x021F,
+ HID_USAGE_CONSUMER_AC_FIND_AND_REPLACE = 0x0220,
// Browser/Explorer Specific
HID_USAGE_CONSUMER_AC_SEARCH = 0x0221,
+ HID_USAGE_CONSUMER_AC_GO_TO = 0x0222,
HID_USAGE_CONSUMER_AC_HOME = 0x0223,
HID_USAGE_CONSUMER_AC_BACK = 0x0224,
HID_USAGE_CONSUMER_AC_FORWARD = 0x0225,
HID_USAGE_CONSUMER_AC_STOP = 0x0226,
HID_USAGE_CONSUMER_AC_REFRESH = 0x0227,
+ HID_USAGE_CONSUMER_AC_PREVIOUS_LINK = 0x0228,
+ HID_USAGE_CONSUMER_AC_NEXT_LINK = 0x0229,
HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A,
-
+ HID_USAGE_CONSUMER_AC_HISTORY = 0x022B,
+ HID_USAGE_CONSUMER_AC_SUBSCRIPTIONS = 0x022C,
+ HID_USAGE_CONSUMER_AC_ZOOM_IN = 0x022D,
+ HID_USAGE_CONSUMER_AC_ZOOM_OUT = 0x022E,
+ HID_USAGE_CONSUMER_AC_ZOOM = 0x022F,
+ HID_USAGE_CONSUMER_AC_FULL_SCREEN_VIEW = 0x0230,
+ HID_USAGE_CONSUMER_AC_NORMAL_VIEW = 0x0231,
+ HID_USAGE_CONSUMER_AC_VIEW_TOGGLE = 0x0232,
+ HID_USAGE_CONSUMER_AC_SCROLL_UP = 0x0233,
+ HID_USAGE_CONSUMER_AC_SCROLL_DOWN = 0x0234,
+ HID_USAGE_CONSUMER_AC_SCROLL = 0x0235,
+ HID_USAGE_CONSUMER_AC_PAN_LEFT = 0x0236,
+ HID_USAGE_CONSUMER_AC_PAN_RIGHT = 0x0237,
// Mouse Horizontal scroll
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
+ HID_USAGE_CONSUMER_AC_NEW_WINDOW = 0x0239,
+ HID_USAGE_CONSUMER_AC_TILE_HORIZONTALLY = 0x023A,
+ HID_USAGE_CONSUMER_AC_TILE_VERTICALLY = 0x023B,
+ HID_USAGE_CONSUMER_AC_FORMAT = 0x023C,
+ HID_USAGE_CONSUMER_AC_EDIT = 0x023D,
+ HID_USAGE_CONSUMER_AC_BOLD = 0x023E,
+ HID_USAGE_CONSUMER_AC_ITALICS = 0x023F,
+ HID_USAGE_CONSUMER_AC_UNDERLINE = 0x0240,
+ HID_USAGE_CONSUMER_AC_STRIKETHROUGH = 0x0241,
+ HID_USAGE_CONSUMER_AC_SUBSCRIPT = 0x0242,
+ HID_USAGE_CONSUMER_AC_SUPERSCRIPT = 0x0243,
+ HID_USAGE_CONSUMER_AC_ALL_CAPS = 0x0244,
+ HID_USAGE_CONSUMER_AC_ROTATE = 0x0245,
+ HID_USAGE_CONSUMER_AC_RESIZE = 0x0246,
+ HID_USAGE_CONSUMER_AC_FLIP_HORIZONTAL = 0x0247,
+ HID_USAGE_CONSUMER_AC_FLIP_VERTICAL = 0x0248,
+ HID_USAGE_CONSUMER_AC_MIRROR_HORIZONTAL = 0x0249,
+ HID_USAGE_CONSUMER_AC_MIRROR_VERTICAL = 0x024A,
+ HID_USAGE_CONSUMER_AC_FONT_SELECT = 0x024B,
+ HID_USAGE_CONSUMER_AC_FONT_COLOR = 0x024C,
+ HID_USAGE_CONSUMER_AC_FONT_SIZE = 0x024D,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_LEFT = 0x024E,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_CENTER_H = 0x024F,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_RIGHT = 0x0250,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_BLOCK_H = 0x0251,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_TOP = 0x0252,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_CENTER_V = 0x0253,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_BOTTOM = 0x0254,
+ HID_USAGE_CONSUMER_AC_JUSTIFY_BLOCK_V = 0x0255,
+ HID_USAGE_CONSUMER_AC_INDENT_DECREASE = 0x0256,
+ HID_USAGE_CONSUMER_AC_INDENT_INCREASE = 0x0257,
+ HID_USAGE_CONSUMER_AC_NUMBERED_LIST = 0x0258,
+ HID_USAGE_CONSUMER_AC_RESTART_NUMBERING = 0x0259,
+ HID_USAGE_CONSUMER_AC_BULLETED_LIST = 0x025A,
+ HID_USAGE_CONSUMER_AC_PROMOTE = 0x025B,
+ HID_USAGE_CONSUMER_AC_DEMOTE = 0x025C,
+ HID_USAGE_CONSUMER_AC_YES = 0x025D,
+ HID_USAGE_CONSUMER_AC_NO = 0x025E,
+ HID_USAGE_CONSUMER_AC_CANCEL = 0x025F,
+ HID_USAGE_CONSUMER_AC_CATALOG = 0x0260,
+ HID_USAGE_CONSUMER_AC_BUY_CHECKOUT = 0x0261,
+ HID_USAGE_CONSUMER_AC_ADD_TO_CART = 0x0262,
+ HID_USAGE_CONSUMER_AC_EXPAND = 0x0263,
+ HID_USAGE_CONSUMER_AC_EXPAND_ALL = 0x0264,
+ HID_USAGE_CONSUMER_AC_COLLAPSE = 0x0265,
+ HID_USAGE_CONSUMER_AC_COLLAPSE_ALL = 0x0266,
+ HID_USAGE_CONSUMER_AC_PRINT_PREVIEW = 0x0267,
+ HID_USAGE_CONSUMER_AC_PASTE_SPECIAL = 0x0268,
+ HID_USAGE_CONSUMER_AC_INSERT_MODE = 0x0269,
+ HID_USAGE_CONSUMER_AC_DELETE = 0x026A,
+ HID_USAGE_CONSUMER_AC_LOCK = 0x026B,
+ HID_USAGE_CONSUMER_AC_UNLOCK = 0x026C,
+ HID_USAGE_CONSUMER_AC_PROTECT = 0x026D,
+ HID_USAGE_CONSUMER_AC_UNPROTECT = 0x026E,
+ HID_USAGE_CONSUMER_AC_ATTACH_COMMENT = 0x026F,
+ HID_USAGE_CONSUMER_AC_DELETE_COMMENT = 0x0270,
+ HID_USAGE_CONSUMER_AC_VIEW_COMMENT = 0x0271,
+ HID_USAGE_CONSUMER_AC_SELECT_WORD = 0x0272,
+ HID_USAGE_CONSUMER_AC_SELECT_SENTENCE = 0x0273,
+ HID_USAGE_CONSUMER_AC_SELECT_PARAGRAPH = 0x0274,
+ HID_USAGE_CONSUMER_AC_SELECT_COLUMN = 0x0275,
+ HID_USAGE_CONSUMER_AC_SELECT_ROW = 0x0276,
+ HID_USAGE_CONSUMER_AC_SELECT_TABLE = 0x0277,
+ HID_USAGE_CONSUMER_AC_SELECT_OBJECT = 0x0278,
+ HID_USAGE_CONSUMER_AC_REDO_REPEAT = 0x0279,
+ HID_USAGE_CONSUMER_AC_SORT = 0x027A,
+ HID_USAGE_CONSUMER_AC_SORT_ASCENDING = 0x027B,
+ HID_USAGE_CONSUMER_AC_SORT_DESCENDING = 0x027C,
+ HID_USAGE_CONSUMER_AC_FILTER = 0x027D,
+ HID_USAGE_CONSUMER_AC_SET_CLOCK = 0x027E,
+ HID_USAGE_CONSUMER_AC_VIEW_CLOCK = 0x027F,
+ HID_USAGE_CONSUMER_AC_SELECT_TIME_ZONE = 0x0280,
+ HID_USAGE_CONSUMER_AC_EDIT_TIME_ZONES = 0x0281,
+ HID_USAGE_CONSUMER_AC_SET_ALARM = 0x0282,
+ HID_USAGE_CONSUMER_AC_CLEAR_ALARM = 0x0283,
+ HID_USAGE_CONSUMER_AC_SNOOZE_ALARM = 0x0284,
+ HID_USAGE_CONSUMER_AC_RESET_ALARM = 0x0285,
+ HID_USAGE_CONSUMER_AC_SYNCHRONIZE = 0x0286,
+ HID_USAGE_CONSUMER_AC_SEND_RECEIVE = 0x0287,
+ HID_USAGE_CONSUMER_AC_SEND_TO = 0x0288,
+ HID_USAGE_CONSUMER_AC_REPLY = 0x0289,
+ HID_USAGE_CONSUMER_AC_REPLY_ALL = 0x028A,
+ HID_USAGE_CONSUMER_AC_FORWARD_MSG = 0x028B,
+ HID_USAGE_CONSUMER_AC_SEND = 0x028C,
+ HID_USAGE_CONSUMER_AC_ATTACH_FILE = 0x028D,
+ HID_USAGE_CONSUMER_AC_UPLOAD = 0x028E,
+ HID_USAGE_CONSUMER_AC_DOWNLOAD_SAVE_TARGET_AS = 0x028F,
+ HID_USAGE_CONSUMER_AC_SET_BORDERS = 0x0290,
+ HID_USAGE_CONSUMER_AC_INSERT_ROW = 0x0291,
+ HID_USAGE_CONSUMER_AC_INSERT_COLUMN = 0x0292,
+ HID_USAGE_CONSUMER_AC_INSERT_FILE = 0x0293,
+ HID_USAGE_CONSUMER_AC_INSERT_PICTURE = 0x0294,
+ HID_USAGE_CONSUMER_AC_INSERT_OBJECT = 0x0295,
+ HID_USAGE_CONSUMER_AC_INSERT_SYMBOL = 0x0296,
+ HID_USAGE_CONSUMER_AC_SAVE_AND_CLOSE = 0x0297,
+ HID_USAGE_CONSUMER_AC_RENAME = 0x0298,
+ HID_USAGE_CONSUMER_AC_MERGE = 0x0299,
+ HID_USAGE_CONSUMER_AC_SPLIT = 0x029A,
+ HID_USAGE_CONSUMER_AC_DISRIBUTE_HORIZONTALLY = 0x029B,
+ HID_USAGE_CONSUMER_AC_DISTRIBUTE_VERTICALLY = 0x029C,
+ // 29D-FFFF Reserved
+
};
/// HID Usage Table: Digitizer Page (0x0D)
@@ -1216,6 +1597,200 @@ enum {
HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
};
+/// HID Usage Table: Power Device Page (0x84)
+enum {
+ HID_USAGE_POWER_UNDEFINED = 0x00,
+ HID_USAGE_POWER_I_NAME = 0x01,
+ HID_USAGE_POWER_PRESENT_STATUS = 0x02,
+ HID_USAGE_POWER_CHANGED_STATUS = 0x03,
+ HID_USAGE_POWER_UPS = 0x04,
+ HID_USAGE_POWER_POWER_SUPPLY = 0x05,
+ // 06-0F Reserved
+ HID_USAGE_POWER_BATTERY_SYSTEM = 0x10,
+ HID_USAGE_POWER_BATTERY_SYSTEM_ID = 0x11,
+ HID_USAGE_POWER_BATTERY = 0x12,
+ HID_USAGE_POWER_BATTERY_ID = 0x13,
+ HID_USAGE_POWER_CHARGER = 0x14,
+ HID_USAGE_POWER_CHARGER_ID = 0x15,
+ HID_USAGE_POWER_POWER_CONVERTER = 0x16,
+ HID_USAGE_POWER_POWER_CONVERTER_ID = 0x17,
+ HID_USAGE_POWER_OUTLET_SYSTEM = 0x18,
+ HID_USAGE_POWER_OUTLET_SYSTEM_ID = 0x19,
+ HID_USAGE_POWER_INPUT = 0x1A,
+ HID_USAGE_POWER_INPUT_ID = 0x1B,
+ HID_USAGE_POWER_OUTPUT = 0x1C,
+ HID_USAGE_POWER_OUTPUT_ID = 0x1D,
+ HID_USAGE_POWER_FLOW = 0x1E,
+ HID_USAGE_POWER_FLOW_ID = 0x1F,
+ HID_USAGE_POWER_OUTLET = 0x20,
+ HID_USAGE_POWER_OUTLET_ID = 0x21,
+ HID_USAGE_POWER_GANG = 0x22,
+ HID_USAGE_POWER_GANG_ID = 0x23,
+ HID_USAGE_POWER_POWER_SUMMARY = 0x24,
+ HID_USAGE_POWER_POWER_SUMMARY_ID = 0x25,
+ // 26-2F Reserved
+ HID_USAGE_POWER_VOLTAGE = 0x30,
+ HID_USAGE_POWER_CURRENT = 0x31,
+ HID_USAGE_POWER_FREQUENCY = 0x32,
+ HID_USAGE_POWER_APPARENT_POWER = 0x33,
+ HID_USAGE_POWER_ACTIVE_POWER = 0x34,
+ HID_USAGE_POWER_PERCENT_LOAD = 0x35,
+ HID_USAGE_POWER_TEMPERATURE = 0x36,
+ HID_USAGE_POWER_HUMIDITY = 0x37,
+ HID_USAGE_POWER_BAD_COUNT = 0x38,
+ // 39-3F Reserved
+ HID_USAGE_POWER_CONFIG_VOLTAGE = 0x40,
+ HID_USAGE_POWER_CONFIG_CURRENT = 0x41,
+ HID_USAGE_POWER_CONFIG_FREQUENCY = 0x42,
+ HID_USAGE_POWER_CONFIG_APPARENT_POWER = 0x43,
+ HID_USAGE_POWER_CONFIG_ACTIVE_POWER = 0x44,
+ HID_USAGE_POWER_CONFIG_PERCENT_LOAD = 0x45,
+ HID_USAGE_POWER_CONFIG_TEMPERATURE = 0x46,
+ HID_USAGE_POWER_CONFIG_HUMIDITY = 0x47,
+ // 48-4F Reserved
+ HID_USAGE_POWER_SWITCH_ON_CONTROL = 0x50,
+ HID_USAGE_POWER_SWITCH_OFF_CONTROL = 0x51,
+ HID_USAGE_POWER_TOGGLE_CONTROL = 0x52,
+ HID_USAGE_POWER_LOW_VOLTAGE_TRANSFER = 0x53,
+ HID_USAGE_POWER_HIGH_VOLTAGE_TRANSFER = 0x54,
+ HID_USAGE_POWER_DELAY_BEFORE_REBOOT = 0x55,
+ HID_USAGE_POWER_DELAY_BEFORE_STARTUP = 0x56,
+ HID_USAGE_POWER_DELAY_BEFORE_SHUTDOWN = 0x57,
+ HID_USAGE_POWER_TEST = 0x58,
+ HID_USAGE_POWER_MODULE_RESET = 0x59,
+ HID_USAGE_POWER_AUDIBLE_ALARM_CONTROL = 0x5A,
+ // 5B-5F Reserved
+ HID_USAGE_POWER_PRESENT = 0x60,
+ HID_USAGE_POWER_GOOD = 0x61,
+ HID_USAGE_POWER_INTERNAL_FAILURE = 0x62,
+ HID_USAGE_POWER_VOLTAGE_OUT_OF_RANGE = 0x63,
+ HID_USAGE_POWER_FREQUENCY_OUT_OF_RANGE = 0x64,
+ HID_USAGE_POWER_OVERLOAD = 0x65,
+ HID_USAGE_POWER_OVER_CHARGED = 0x66,
+ HID_USAGE_POWER_OVER_TEMPERATURE = 0x67,
+ HID_USAGE_POWER_SHUTDOWN_REQUESTED = 0x68,
+ HID_USAGE_POWER_SHUTDOWN_IMMINENT = 0x69,
+ // 6A Reserved
+ HID_USAGE_POWER_SWITCH_ON_OFF = 0x6B,
+ HID_USAGE_POWER_SWITCHABLE = 0x6C,
+ HID_USAGE_POWER_USED = 0x6D,
+ HID_USAGE_POWER_BOOST = 0x6E,
+ HID_USAGE_POWER_BUCK = 0x6F,
+ HID_USAGE_POWER_INITIALIZED = 0x70,
+ HID_USAGE_POWER_TESTED = 0x71,
+ HID_USAGE_POWER_AWAITING_POWER = 0x72,
+ HID_USAGE_POWER_COMMUNICATION_LOST = 0x73,
+ // 74-FC Reserved
+ HID_USAGE_POWER_I_MANUFACTURER = 0xFD,
+ HID_USAGE_POWER_I_PRODUCT = 0xFE,
+ HID_USAGE_POWER_I_SERIAL_NUMBER = 0xFF
+};
+
+/// HID Usage Table: Battery System Page (0x85)
+enum {
+ HID_USAGE_BATTERY_UNDEFINED = 0x00,
+ HID_USAGE_BATTERY_SMB_BATTERY_MODE = 0x01,
+ HID_USAGE_BATTERY_SMB_BATTERY_STATUS = 0x02,
+ HID_USAGE_BATTERY_SMB_ALARM_WARNING = 0x03,
+ HID_USAGE_BATTERY_SMB_CHARGER_MODE = 0x04,
+ HID_USAGE_BATTERY_SMB_CHARGER_STATUS = 0x05,
+ HID_USAGE_BATTERY_SMB_CHARGER_SPEC_INFO = 0x06,
+ HID_USAGE_BATTERY_SMB_SELECTOR_STATE = 0x07,
+ HID_USAGE_BATTERY_SMB_SELECTOR_PRESETS = 0x08,
+ HID_USAGE_BATTERY_SMB_SELECTOR_INFO = 0x09,
+ // 0A-0F Reserved
+ HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_1 = 0x10,
+ HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_2 = 0x11,
+ HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_3 = 0x12,
+ HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_4 = 0x13,
+ HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_5 = 0x14,
+ HID_USAGE_BATTERY_CONNECTION_TO_SMBUS = 0x15,
+ HID_USAGE_BATTERY_OUTPUT_CONNECTION = 0x16,
+ HID_USAGE_BATTERY_CHARGER_CONNECTION = 0x17,
+ HID_USAGE_BATTERY_BATTERY_INSERTION = 0x18,
+ HID_USAGE_BATTERY_USE_NEXT = 0x19,
+ HID_USAGE_BATTERY_OK_TO_USE = 0x1A,
+ HID_USAGE_BATTERY_BATTERY_SUPPORTED = 0x1B,
+ HID_USAGE_BATTERY_SELECTOR_REVISION = 0x1C,
+ HID_USAGE_BATTERY_CHARGING_INDICATOR = 0x1D,
+ // 1E-27 Reserved
+ HID_USAGE_BATTERY_MANUFACTURER_ACCESS = 0x28,
+ HID_USAGE_BATTERY_REMAINING_CAPACITY_LIMIT = 0x29,
+ HID_USAGE_BATTERY_REMAINING_TIME_LIMIT = 0x2A,
+ HID_USAGE_BATTERY_AT_RATE = 0x2B,
+ HID_USAGE_BATTERY_CAPACITY_MODE = 0x2C,
+ HID_USAGE_BATTERY_BROADCAST_TO_CHARGER = 0x2D,
+ HID_USAGE_BATTERY_PRIMARY_BATTERY = 0x2E,
+ HID_USAGE_BATTERY_CHARGE_CONTROLLER = 0x2F,
+ // 30-3F Reserved
+ HID_USAGE_BATTERY_TERMINATE_CHARGE = 0x40,
+ HID_USAGE_BATTERY_TERMINATE_DISCHARGE = 0x41,
+ HID_USAGE_BATTERY_BELOW_REMAINING_CAPACITY_LIMIT = 0x42,
+ HID_USAGE_BATTERY_REMAINING_TIME_LIMIT_EXPIRED = 0x43,
+ HID_USAGE_BATTERY_CHARGING = 0x44,
+ HID_USAGE_BATTERY_DISCHARGING = 0x45,
+ HID_USAGE_BATTERY_FULLY_CHARGED = 0x46,
+ HID_USAGE_BATTERY_FULLY_DISCHARGED = 0x47,
+ HID_USAGE_BATTERY_CONDITIONING_FLAG = 0x48,
+ HID_USAGE_BATTERY_AT_RATE_OK = 0x49,
+ HID_USAGE_BATTERY_SMB_ERROR_CODE = 0x4A,
+ HID_USAGE_BATTERY_NEED_REPLACEMENT = 0x4B,
+ // 4C-5F Reserved
+ HID_USAGE_BATTERY_AT_RATE_TIME_TO_FULL = 0x60,
+ HID_USAGE_BATTERY_AT_RATE_TIME_TO_EMPTY = 0x61,
+ HID_USAGE_BATTERY_AVERAGE_CURRENT = 0x62,
+ HID_USAGE_BATTERY_MAX_ERROR = 0x63,
+ HID_USAGE_BATTERY_RELATIVE_STATE_OF_CHARGE = 0x64,
+ HID_USAGE_BATTERY_ABSOLUTE_STATE_OF_CHARGE = 0x65,
+ HID_USAGE_BATTERY_REMAINING_CAPACITY = 0x66,
+ HID_USAGE_BATTERY_FULL_CHARGE_CAPACITY = 0x67,
+ HID_USAGE_BATTERY_RUN_TIME_TO_EMPTY = 0x68,
+ HID_USAGE_BATTERY_AVERAGE_TIME_TO_EMPTY = 0x69,
+ HID_USAGE_BATTERY_AVERAGE_TIME_TO_FULL = 0x6A,
+ HID_USAGE_BATTERY_CYCLE_COUNT = 0x6B,
+ // 6C-7F Reserved
+ HID_USAGE_BATTERY_BATT_PACK_MODEL_LEVEL = 0x80,
+ HID_USAGE_BATTERY_INTERNAL_CHARGE_CONTROLLER = 0x81,
+ HID_USAGE_BATTERY_PRIMARY_BATTERY_SUPPORT = 0x82,
+ HID_USAGE_BATTERY_DESIGN_CAPACITY = 0x83,
+ HID_USAGE_BATTERY_SPECIFICATION_INFO = 0x84,
+ HID_USAGE_BATTERY_MANUFACTURER_DATE = 0x85,
+ HID_USAGE_BATTERY_SERIAL_NUMBER = 0x86,
+ HID_USAGE_BATTERY_I_MANUFACTURER_NAME = 0x87,
+ HID_USAGE_BATTERY_I_DEVICE_NAME = 0x88,
+ HID_USAGE_BATTERY_I_DEVICE_CHEMISTRY = 0x89,
+ HID_USAGE_BATTERY_MANUFACTURER_DATA = 0x8A,
+ HID_USAGE_BATTERY_RECHARGEABLE = 0x8B,
+ HID_USAGE_BATTERY_WARNING_CAPACITY_LIMIT = 0x8C,
+ HID_USAGE_BATTERY_CAPACITY_GRANULARITY_1 = 0x8D,
+ HID_USAGE_BATTERY_CAPACITY_GRANULARITY_2 = 0x8E,
+ HID_USAGE_BATTERY_I_OEMINFORMATION = 0x8F,
+ // 90-BF Reserved
+ HID_USAGE_BATTERY_INHIBIT_CHARGE = 0xC0,
+ HID_USAGE_BATTERY_ENABLE_POLLING = 0xC1,
+ HID_USAGE_BATTERY_RESET_TO_ZERO = 0xC2,
+ // C3-CF Reserved
+ HID_USAGE_BATTERY_AC_PRESENT = 0xD0,
+ HID_USAGE_BATTERY_BATTERY_PRESENT = 0xD1,
+ HID_USAGE_BATTERY_POWER_FAIL = 0xD2,
+ HID_USAGE_BATTERY_ALARM_INHIBITED = 0xD3,
+ HID_USAGE_BATTERY_THERMISTOR_UNDER_RANGE = 0xD4,
+ HID_USAGE_BATTERY_THERMISTOR_HOT = 0xD5,
+ HID_USAGE_BATTERY_THERMISTOR_COLD = 0xD6,
+ HID_USAGE_BATTERY_THERMISTOR_OVER_RANGE = 0xD7,
+ HID_USAGE_BATTERY_VOLTAGE_OUT_OF_RANGE = 0xD8,
+ HID_USAGE_BATTERY_CURRENT_OUT_OF_RANGE = 0xD9,
+ HID_USAGE_BATTERY_CURRENT_NOT_REGULATED = 0xDA,
+ HID_USAGE_BATTERY_VOLTAGE_NOT_REGULATED = 0xDB,
+ HID_USAGE_BATTERY_MASTER_MODE = 0xDC,
+ // DD-EF Reserved
+ HID_USAGE_BATTERY_CHARGER_SELECTOR_SUPPORT = 0xF0,
+ HID_USAGE_BATTERY_CHARGER_SPEC = 0xF1,
+ HID_USAGE_BATTERY_LEVEL_2 = 0xF2,
+ HID_USAGE_BATTERY_LEVEL_3 = 0xF3
+ // F4-FF Reserved
+};
+
/// HID Usage Table: FIDO Alliance Page (0xF1D0)
enum {
HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
@@ -1496,6 +2071,6 @@ enum {
}
#endif
-#endif /* _TUSB_HID_H__ */
+#endif /* TUSB_HID_H__ */
/// @}
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index fc1dbcbd8..32b572973 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -267,14 +267,14 @@ void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, u
// Stylus Pen Report Descriptor Template
#define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \
- HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \
+ HID_USAGE ( HID_USAGE_DIGITIZER_PEN ) , \
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \
/* Report ID if any */\
__VA_ARGS__ \
- HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \
- HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \
- HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \
- HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \
+ HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ), \
+ HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), \
+ HID_USAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ), \
+ HID_USAGE ( HID_USAGE_DIGITIZER_IN_RANGE ), \
HID_LOGICAL_MIN ( 0 ), \
HID_LOGICAL_MAX ( 1 ), \
HID_REPORT_SIZE ( 1 ), \
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index 57e437196..da776d04c 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -71,6 +71,50 @@ 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) {
@@ -183,9 +227,7 @@ static void set_protocol_complete(tuh_xfer_t* xfer) {
p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
}
- if (tuh_hid_set_protocol_complete_cb) {
- tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
- }
+ tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
}
void tuh_hid_set_default_protocol(uint8_t protocol) {
@@ -230,16 +272,14 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
static void get_report_complete(tuh_xfer_t* xfer) {
TU_LOG_DRV("HID Get Report complete\r\n");
- if (tuh_hid_get_report_complete_cb) {
- 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 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_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);
- }
+ 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) {
@@ -274,16 +314,14 @@ bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r
static void set_report_complete(tuh_xfer_t* xfer) {
TU_LOG_DRV("HID Set Report complete\r\n");
- if (tuh_hid_set_report_complete_cb) {
- 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 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);
+ 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);
- }
+ 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) {
@@ -448,9 +486,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2);
tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
} else {
- if (tuh_hid_report_sent_cb) {
- tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes);
- }
+ tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes);
}
return true;
@@ -461,9 +497,7 @@ void hidh_close(uint8_t daddr) {
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);
- if (tuh_hid_umount_cb) {
- tuh_hid_umount_cb(daddr, i);
- }
+ tuh_hid_umount_cb(daddr, i);
tu_memclr(p_hid, sizeof(hidh_interface_t));
}
}
@@ -519,7 +553,8 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_
// Assume bNumDescriptors = 1
p_hid->report_desc_type = desc_hid->bReportType;
- p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
+ // 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;
@@ -624,7 +659,7 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con
p_hid->mounted = true;
// enumeration is complete
- if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
+ 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);
@@ -662,9 +697,12 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
uint8_t const tag = header.tag;
uint8_t const type = header.type;
- uint8_t const size = header.size;
+ 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 = desc_report[0];
+ 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++) {
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index 9681c704b..87f0e7dc9 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_HID_HOST_H_
-#define _TUSB_HID_HOST_H_
+#ifndef TUSB_HID_HOST_H_
+#define TUSB_HID_HOST_H_
#include "hid.h"
@@ -145,28 +145,28 @@ bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
-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 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
-TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx);
+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
-TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
+void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, 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
-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 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
-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 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
-TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
+void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
//--------------------------------------------------------------------+
// Internal Class Driver API
@@ -182,4 +182,4 @@ void hidh_close(uint8_t dev_addr);
}
#endif
-#endif /* _TUSB_HID_HOST_H_ */
+#endif /* TUSB_HID_HOST_H_ */
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 0bbb3caf4..b20903d68 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -37,12 +37,18 @@
#include "midi_device.h"
//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf) {
+ (void)itf;
+}
+
+//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
+ uint8_t rhport;
uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
// For Stream read()/write() API
// Messages are always 4 bytes long, queue them for reading and writing so the
@@ -51,126 +57,97 @@ typedef struct {
midi_driver_stream_t stream_read;
/*------------- From this point, data is not cleared by bus reset -------------*/
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
- uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
+ // Endpoint stream
+ struct {
+ tu_edpt_stream_t tx;
+ tu_edpt_stream_t rx;
- #if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
- #endif
+ uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
+ uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
+ } ep_stream;
} midid_interface_t;
-#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
+#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, ep_stream)
+
+static midid_interface_t _midid_itf[CFG_TUD_MIDI];
// Endpoint Transfer buffer
-CFG_TUD_MEM_SECTION static struct {
+typedef struct {
TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
-} _midid_epbuf[CFG_TUD_MIDI];
+} midid_epbuf_t;
+
+CFG_TUD_MEM_SECTION static midid_epbuf_t _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;
-}
+ midid_interface_t *p_midi = &_midid_itf[itf];
-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);
- }
+ const bool tx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.tx);
+ const bool rx_opened = tu_edpt_stream_is_opened(&p_midi->ep_stream.rx);
+ return tx_opened && rx_opened;
}
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
-uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
-{
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num) {
(void) cable_num;
-
- midid_interface_t* midi = &_midid_itf[itf];
- const midi_driver_stream_t* stream = &midi->stream_read;
+ const midid_interface_t *p_midi = &_midid_itf[itf];
+ const midi_driver_stream_t *stream = &p_midi->stream_read;
+ const tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
// when using with packet API stream total & index are both zero
- return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
+ return tu_edpt_stream_read_available(ep_str) + (uint8_t)(stream->total - stream->index);
}
-uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
-{
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize) {
(void) cable_num;
- TU_VERIFY(bufsize, 0);
+ TU_VERIFY(buffer != NULL && bufsize > 0, 0);
- uint8_t* buf8 = (uint8_t*) buffer;
-
- midid_interface_t* midi = &_midid_itf[itf];
- midi_driver_stream_t* stream = &midi->stream_read;
+ uint8_t *buf8 = (uint8_t *)buffer;
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_read;
uint32_t total_read = 0;
- while( bufsize )
- {
+ while (bufsize > 0) {
// Get new packet from fifo, then set packet expected bytes
- if ( stream->total == 0 )
- {
- // return if there is no more data from fifo
- if ( !tud_midi_n_packet_read(itf, stream->buffer) ) return total_read;
+ if (stream->total == 0) {
+ if (!tud_midi_n_packet_read(itf, stream->buffer)) {
+ return total_read; // return if there is no more data from fifo
+ }
- uint8_t const code_index = stream->buffer[0] & 0x0f;
+ const uint8_t code_index = stream->buffer[0] & 0x0f;
// MIDI 1.0 Table 4-1: Code Index Number Classifications
- switch(code_index)
- {
+ switch (code_index) {
case MIDI_CIN_MISC:
case MIDI_CIN_CABLE_EVENT:
// These are reserved and unused, possibly issue somewhere, skip this packet
return 0;
- break;
case MIDI_CIN_SYSEX_END_1BYTE:
case MIDI_CIN_1BYTE_DATA:
stream->total = 1;
- break;
+ break;
case MIDI_CIN_SYSCOM_2BYTE :
case MIDI_CIN_SYSEX_END_2BYTE :
case MIDI_CIN_PROGRAM_CHANGE :
case MIDI_CIN_CHANNEL_PRESSURE :
stream->total = 2;
- break;
+ break;
default:
stream->total = 3;
- break;
+ break;
}
}
// Copy data up to bufsize
- uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize);
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize);
// Skip the header (1st byte) in the buffer
TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
@@ -181,8 +158,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
bufsize -= count;
// complete current event packet, reset stream
- if ( stream->total == stream->index )
- {
+ if (stream->total == stream->index) {
stream->index = 0;
stream->total = 0;
}
@@ -191,150 +167,107 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
return total_read;
}
-bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
-{
- midid_interface_t* midi = &_midid_itf[itf];
- TU_VERIFY(midi->ep_out);
+bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str));
+ return 4 == tu_edpt_stream_read(p_midi->rhport, ep_str, packet, 4);
+}
- const uint32_t num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
- _prep_out_transaction(itf);
- return (num_read == 4);
+uint32_t tud_midi_n_packet_read_n(uint8_t itf, uint8_t packets[], uint32_t max_packets) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
+
+ const uint32_t num_read = tu_edpt_stream_read(p_midi->rhport, ep_str, packets, 4u * max_packets);
+ return num_read >> 2u;
}
//--------------------------------------------------------------------+
// 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 tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_write;
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
uint32_t i = 0;
- while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
- {
+ while (i < bufsize) {
+ if (tu_edpt_stream_write_available(p_midi->rhport, ep_str) < 4) {
+ break;
+ }
+
const uint8_t data = buffer[i];
i++;
- if ( stream->index == 0 )
- {
+ if (stream->index == 0) {
//------------- New event packet -------------//
const uint8_t msg = data >> 4;
- stream->index = 2;
+ stream->index = 2;
stream->buffer[1] = data;
// Check to see if we're still in a SysEx transmit.
- if ( ((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START )
- {
- if ( data == MIDI_STATUS_SYSEX_END )
- {
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
- stream->total = 2;
- }
- else
- {
+ if (((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START) {
+ if (data == MIDI_STATUS_SYSEX_END) {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
+ stream->total = 2;
+ } else {
stream->total = 4;
}
- }
- else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
- {
+ } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
// Channel Voice Messages
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
- stream->total = 4;
- }
- else if ( msg == 0xC || msg == 0xD)
- {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | msg);
+ stream->total = 4;
+ } else if (msg == 0xC || msg == 0xD) {
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
- stream->total = 3;
- }
- else if ( msg == 0xf )
- {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | msg);
+ stream->total = 3;
+ } else if (msg == 0xf) {
// System message
- if ( data == MIDI_STATUS_SYSEX_START )
- {
+ if (data == MIDI_STATUS_SYSEX_START) {
stream->buffer[0] = MIDI_CIN_SYSEX_START;
- stream->total = 4;
- }
- else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT )
- {
+ stream->total = 4;
+ } else if (data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT) {
stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
- stream->total = 3;
- }
- else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER )
- {
+ stream->total = 3;
+ } else if (data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER) {
stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
- stream->total = 4;
- }
- else
- {
+ stream->total = 4;
+ } else {
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
- stream->total = 2;
+ stream->total = 2;
}
stream->buffer[0] |= (uint8_t)(cable_num << 4);
- }
- else
- {
+ } else {
// Pack individual bytes if we don't support packing them into words.
- stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
+ stream->buffer[0] = (uint8_t)(cable_num << 4 | 0xf);
stream->buffer[2] = 0;
stream->buffer[3] = 0;
- stream->index = 2;
- stream->total = 2;
+ stream->total = 2; // index already set to 2
}
- }
- else
- {
+ } else {
//------------- On-going (buffering) packet -------------//
-
TU_ASSERT(stream->index < 4, i);
stream->buffer[stream->index] = data;
stream->index++;
// See if this byte ends a SysEx.
- if ( (stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
- {
- stream->buffer[0] = (uint8_t) ((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
- stream->total = stream->index;
+ if ((stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
+ stream->buffer[0] = (uint8_t)((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
+ stream->total = stream->index;
}
}
// Send out packet
- if ( stream->index == stream->total )
- {
+ if (stream->index == stream->total) {
// zeroes unused bytes
for (uint8_t idx = stream->total; idx < 4; idx++) {
stream->buffer[idx] = 0;
}
- const uint16_t count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+ const uint32_t count = tu_edpt_stream_write(p_midi->rhport, ep_str, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = stream->total = 0;
@@ -344,25 +277,37 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t*
}
}
- write_flush(itf);
+ (void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
return i;
}
bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
- midid_interface_t* midi = &_midid_itf[itf];
- TU_VERIFY(midi->ep_in);
-
- if (tu_fifo_remaining(&midi->tx_ff) < 4) {
- return false;
- }
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str));
- tu_fifo_write_n(&midi->tx_ff, packet, 4);
- write_flush(itf);
+ TU_VERIFY(tu_edpt_stream_write_available(p_midi->rhport, ep_str) >= 4);
+ TU_VERIFY(tu_edpt_stream_write(p_midi->rhport, ep_str, packet, 4) > 0);
+ (void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
return true;
}
+uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_t n_packets) {
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.tx;
+ TU_VERIFY(tu_edpt_stream_is_opened(ep_str), 0);
+
+ uint32_t n_bytes = tu_edpt_stream_write_available(p_midi->rhport, ep_str);
+ n_bytes = tu_min32(tu_align4(n_bytes), n_packets << 2u);
+
+ const uint32_t n_write = tu_edpt_stream_write(p_midi->rhport, ep_str, packets, n_bytes);
+ (void)tu_edpt_stream_write_xfer(p_midi->rhport, ep_str);
+
+ return n_write >> 2u;
+}
+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
@@ -370,72 +315,64 @@ void midid_init(void) {
tu_memclr(_midid_itf, sizeof(_midid_itf));
for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
- midid_interface_t* midi = &_midid_itf[i];
+ midid_interface_t *p_midi = &_midid_itf[i];
+ midid_epbuf_t *p_epbuf = &_midid_epbuf[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.
+ tu_edpt_stream_init(
+ &p_midi->ep_stream.rx, false, false, false, p_midi->ep_stream.rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE,
+ p_epbuf->epout, CFG_TUD_MIDI_EP_BUFSIZE);
- #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
+ tu_edpt_stream_init(
+ &p_midi->ep_stream.tx, false, true, false, p_midi->ep_stream.tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, p_epbuf->epin,
+ CFG_TUD_MIDI_EP_BUFSIZE);
}
}
bool midid_deinit(void) {
- #if CFG_FIFO_MUTEX
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++) {
- midid_interface_t* midi = &_midid_itf[i];
- osal_mutex_t mutex_rd = midi->rx_ff.mutex_rd;
- osal_mutex_t mutex_wr = midi->tx_ff.mutex_wr;
-
- if (mutex_rd) {
- osal_mutex_delete(mutex_rd);
- tu_fifo_config_mutex(&midi->rx_ff, NULL, NULL);
- }
-
- if (mutex_wr) {
- osal_mutex_delete(mutex_wr);
- tu_fifo_config_mutex(&midi->tx_ff, NULL, NULL);
- }
+ for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
+ midid_interface_t *p_midi = &_midid_itf[i];
+ tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
+ tu_edpt_stream_deinit(&p_midi->ep_stream.tx);
}
- #endif
-
return true;
}
-void midid_reset(uint8_t rhport)
-{
- (void) rhport;
+void midid_reset(uint8_t rhport) {
+ (void)rhport;
+ for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
+ midid_interface_t *p_midi = &_midid_itf[i];
+ tu_memclr(p_midi, ITF_MEM_RESET_SIZE);
+
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
+ tu_edpt_stream_close(&p_midi->ep_stream.rx);
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- midid_interface_t* midi = &_midid_itf[i];
- tu_memclr(midi, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&midi->rx_ff);
- tu_fifo_clear(&midi->tx_ff);
+ tu_edpt_stream_clear(&p_midi->ep_stream.tx);
+ tu_edpt_stream_close(&p_midi->ep_stream.tx);
}
}
-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;
+TU_ATTR_ALWAYS_INLINE static inline uint8_t find_midi_itf(uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUD_MIDI; idx++) {
+ const midid_interface_t *p_midi = &_midid_itf[idx];
+ if (ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *desc_itf, uint16_t max_len) {
+ const uint8_t *p_desc = (const uint8_t *)desc_itf;
+ const uint8_t *desc_end = p_desc + max_len;
// 1st Interface is Audio Control v1 (optional)
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
- drv_len = tu_desc_len(desc_itf);
p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
- while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ while (tu_desc_in_bounds(p_desc, desc_end) && TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) {
+ p_desc = tu_desc_next(p_desc);
}
}
@@ -443,59 +380,48 @@ uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint1
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
- AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
- AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
+ AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol,
+ 0);
- // Find available interface
- midid_interface_t * p_midi = NULL;
- uint8_t idx;
- for(idx=0; idx<CFG_TUD_MIDI; idx++) {
- if ( _midid_itf[idx].ep_in == 0 && _midid_itf[idx].ep_out == 0 ) {
- p_midi = &_midid_itf[idx];
- break;
- }
- }
- TU_ASSERT(p_midi);
+ uint8_t idx = find_midi_itf(0); // find unused interface
+ TU_ASSERT(idx < CFG_TUD_MIDI, 0);
+ midid_interface_t *p_midi = &_midid_itf[idx];
+ p_midi->rhport = rhport;
p_midi->itf_num = desc_midi->bInterfaceNumber;
(void) p_midi->itf_num;
- // next descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
// Find and open endpoint descriptors
- uint8_t found_endpoints = 0;
- while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
- {
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- TU_ASSERT(usbd_edpt_open(rhport, (const tusb_desc_endpoint_t*) p_desc), 0);
- uint8_t ep_addr = ((const tusb_desc_endpoint_t*) p_desc)->bEndpointAddress;
+ uint8_t found_ep = 0;
+ while ((found_ep < desc_midi->bNumEndpoints) && tu_desc_in_bounds(p_desc, desc_end)) {
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
+ const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc;
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
+ const uint8_t ep_addr = ((const tusb_desc_endpoint_t *)p_desc)->bEndpointAddress;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
- {
- p_midi->ep_in = ep_addr;
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p_midi->ep_stream.tx;
+ tu_edpt_stream_open(stream_tx, desc_ep);
+ tu_edpt_stream_clear(stream_tx);
} else {
- p_midi->ep_out = ep_addr;
+ tu_edpt_stream_t *stream_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_open(stream_rx, desc_ep);
+ tu_edpt_stream_clear(stream_rx);
+ TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare to receive data
}
- // Class Specific MIDI Stream endpoint descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
-
- found_endpoints += 1;
+ p_desc = tu_desc_next(p_desc); // skip CS Endpoint descriptor
+ found_ep++;
}
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- // Prepare for incoming data
- _prep_out_transaction(idx);
-
- return drv_len;
+ return (uint16_t)(p_desc - (const uint8_t *)desc_itf);
}
// Invoked when a control transfer occurred on an interface of this class
@@ -506,46 +432,31 @@ bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_req
return false; // driver doesn't support any request yet
}
-bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) result;
- (void) rhport;
-
- uint8_t idx;
- midid_interface_t* p_midi;
+bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void)result;
- // Identify which interface to use
- for (idx = 0; idx < CFG_TUD_MIDI; idx++) {
- p_midi = &_midid_itf[idx];
- if ((ep_addr == p_midi->ep_out) || (ep_addr == p_midi->ep_in)) {
- break;
- }
- }
+ uint8_t idx = find_midi_itf(ep_addr);
TU_ASSERT(idx < CFG_TUD_MIDI);
+ midid_interface_t *p_midi = &_midid_itf[idx];
- // receive new data
- if (ep_addr == p_midi->ep_out) {
- tu_fifo_write_n(&p_midi->rx_ff, _midid_epbuf[idx].epout, (uint16_t)xferred_bytes);
+ tu_edpt_stream_t *ep_st_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_t *ep_st_tx = &p_midi->ep_stream.tx;
- // invoke receive callback if available
- if (tud_midi_rx_cb) {
- tud_midi_rx_cb(idx);
+ if (ep_addr == ep_st_rx->ep_addr) {
+ // Received new data: put into stream's fifo
+ if (result == XFER_RESULT_SUCCESS) {
+ tu_edpt_stream_read_xfer_complete(ep_st_rx, xferred_bytes);
+ tud_midi_rx_cb(idx); // invoke callback
}
-
- // 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);
- }
- }
+ tu_edpt_stream_read_xfer(rhport, ep_st_rx); // prepare for next data
+ } else if (ep_addr == ep_st_tx->ep_addr && result == XFER_RESULT_SUCCESS) {
+ // sent complete: try to send more if possible
+ if (0 == tu_edpt_stream_write_xfer(rhport, ep_st_tx)) {
+ // If there is no data left, a ZLP should be sent if needed
+ (void)tu_edpt_stream_write_zlp_if_needed(rhport, ep_st_tx, xferred_bytes);
}
+ } else {
+ return false;
}
return true;
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index 3e89cc0a3..ddbc2f9f0 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MIDI_DEVICE_H_
-#define _TUSB_MIDI_DEVICE_H_
+#ifndef TUSB_MIDI_DEVICE_H_
+#define TUSB_MIDI_DEVICE_H_
#include "class/audio/audio.h"
#include "midi.h"
@@ -36,21 +36,21 @@
#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
+ #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)
+ #define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#endif
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-/** \addtogroup MIDI_Serial Serial
- * @{
- * \defgroup MIDI_Serial_Device Device
- * @{ */
+//--------------------------------------------------------------------+
+// Application Callback API (optional)
+//--------------------------------------------------------------------+
+void tud_midi_rx_cb(uint8_t itf);
//--------------------------------------------------------------------+
// Application API (Multiple Interfaces)
@@ -58,117 +58,77 @@
//--------------------------------------------------------------------+
// Check if midi interface is mounted
-bool tud_midi_n_mounted (uint8_t itf);
+bool tud_midi_n_mounted(uint8_t itf);
// Get the number of bytes available for reading
-uint32_t tud_midi_n_available (uint8_t itf, uint8_t cable_num);
-
-// Read byte stream (legacy)
-uint32_t tud_midi_n_stream_read (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize);
-
-// 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);
+uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num);
-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);
-}
+// Read byte stream (legacy)
+uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t 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);
-}
+// Write byte Stream (legacy)
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize);
+// Read an event 4-byte packet
+bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]);
-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);
-}
+// Read multiple event packets, return number of read packets
+uint32_t tud_midi_n_packet_read_n(uint8_t itf, uint8_t packets[], uint32_t max_packets);
-TU_ATTR_DEPRECATED("tud_midi_receive() is renamed to tud_midi_packet_read()")
-static inline bool tud_midi_receive(uint8_t packet[4])
-{
- return tud_midi_packet_read(packet);
-}
+// Write an event 4-byte packet
+bool tud_midi_n_packet_write(uint8_t itf, const uint8_t packet[4]);
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
-TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
+// Write multiple event packets, return number of written packets
+uint32_t tud_midi_n_packet_write_n(uint8_t itf, const uint8_t packets[], uint32_t n_packets);
//--------------------------------------------------------------------+
-// Inline Functions
+// Application API (Single Interface)
//--------------------------------------------------------------------+
-
-static inline bool tud_midi_mounted (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_mounted(void) {
return tud_midi_n_mounted(0);
}
-static inline uint32_t tud_midi_available (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_available(void) {
return tud_midi_n_available(0, 0);
}
-static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_read(void *buffer, uint32_t bufsize) {
return tud_midi_n_stream_read(0, 0, buffer, bufsize);
}
-static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi_stream_write(uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
}
-static inline bool tud_midi_packet_read (uint8_t packet[4])
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_packet_read(uint8_t packet[4]) {
return tud_midi_n_packet_read(0, packet);
}
-static inline bool tud_midi_packet_write (uint8_t const packet[4])
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_packet_read_n(uint8_t packets[], uint32_t max_packets) {
+ return tud_midi_n_packet_read_n(0, packets, max_packets);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_midi_packet_write(const uint8_t packet[4]) {
return tud_midi_n_packet_write(0, packet);
}
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_packet_write_n(const uint8_t packets[], uint32_t n_packets) {
+ return tud_midi_n_packet_write_n(0, packets, n_packets);
+}
+
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void midid_init (void);
-bool midid_deinit (void);
-void midid_reset (uint8_t rhport);
-uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
-bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+void midid_init(void);
+bool midid_deinit(void);
+void midid_reset(uint8_t rhport);
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t *request);
+bool midid_xfer_cb(uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_MIDI_DEVICE_H_ */
-
-/** @} */
-/** @} */
+#endif
diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c
index cd6e115ee..07062875c 100644
--- a/src/class/midi/midi_host.c
+++ b/src/class/midi/midi_host.c
@@ -59,9 +59,6 @@ typedef struct {
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
@@ -147,8 +144,6 @@ void midih_close(uint8_t daddr) {
TU_LOG_DRV(" MIDI close addr = %u index = %u\r\n", daddr, idx);
tuh_midi_umount_cb(idx);
- p_midi->ep_in = 0;
- p_midi->ep_out = 0;
p_midi->bInterfaceNumber = 0;
p_midi->rx_cable_count = 0;
p_midi->tx_cable_count = 0;
@@ -169,23 +164,25 @@ bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
+ tu_edpt_stream_t *ep_str_rx = &p_midi->ep_stream.rx;
+ tu_edpt_stream_t *ep_str_tx = &p_midi->ep_stream.tx;
- if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
+ if (ep_addr == ep_str_rx->ep_addr) {
// receive new data, put it into FIFO and invoke callback if available
// Note: some devices send back all zero packets even if there is no data ready
- if (xferred_bytes && !tu_mem_is_zero(p_midi->ep_stream.rx.ep_buf, xferred_bytes)) {
- tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
+ if (xferred_bytes && !tu_mem_is_zero(ep_str_rx->ep_buf, xferred_bytes)) {
+ tu_edpt_stream_read_xfer_complete(ep_str_rx, xferred_bytes);
tuh_midi_rx_cb(idx, xferred_bytes);
}
- tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for next transfer
- } else if (ep_addr == p_midi->ep_stream.tx.ep_addr) {
+ tu_edpt_stream_read_xfer(dev_addr, ep_str_rx); // prepare for next transfer
+ } else if (ep_addr == ep_str_tx->ep_addr) {
tuh_midi_tx_cb(idx, xferred_bytes);
- if (0 == tu_edpt_stream_write_xfer(dev_addr, &p_midi->ep_stream.tx)) {
+ if (0 == tu_edpt_stream_write_xfer(dev_addr, ep_str_tx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
- tu_edpt_stream_write_zlp_if_needed(dev_addr, &p_midi->ep_stream.tx, xferred_bytes);
+ tu_edpt_stream_write_zlp_if_needed(dev_addr, ep_str_tx, xferred_bytes);
}
}
@@ -211,19 +208,36 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
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, skip it
+ // - 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));
+ TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(midi10_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);
+ tu_desc_subtype(p_desc) == AUDIO10_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;
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
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);
@@ -236,7 +250,7 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
p_desc = tu_desc_next(p_desc); // next to CS Header
bool found_new_interface = false;
- while ((p_desc < p_end) && (tu_desc_next(p_desc) <= p_end) && !found_new_interface) {
+ 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;
@@ -278,21 +292,20 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
+ tu_edpt_stream_t *ep_stream;
if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
- p_midi->ep_out = p_ep->bEndpointAddress;
p_midi->tx_cable_count = p_csep->bNumEmbMIDIJack;
desc_cb.desc_epout = p_ep;
-
- TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
- tu_edpt_stream_open(&p_midi->ep_stream.tx, p_ep);
+ ep_stream = &p_midi->ep_stream.tx;
} else {
- p_midi->ep_in = p_ep->bEndpointAddress;
p_midi->rx_cable_count = p_csep->bNumEmbMIDIJack;
- desc_cb.desc_epin = p_ep;
-
- TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
- tu_edpt_stream_open(&p_midi->ep_stream.rx, p_ep);
+ desc_cb.desc_epin = p_ep;
+ ep_stream = &p_midi->ep_stream.rx;
}
+ TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
+ tu_edpt_stream_open(ep_stream, p_ep);
+ tu_edpt_stream_clear(ep_stream);
+
break;
}
@@ -362,8 +375,14 @@ bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
desc->bDescriptorType = TUSB_DESC_INTERFACE;
desc->bInterfaceNumber = p_midi->bInterfaceNumber;
- desc->bAlternateSetting = 0;
- desc->bNumEndpoints = (uint8_t)((p_midi->ep_in != 0 ? 1:0) + (p_midi->ep_out != 0 ? 1:0));
+ desc->bAlternateSetting = 0;
+ desc->bNumEndpoints = 0;
+ if (tu_edpt_stream_is_opened(&p_midi->ep_stream.tx)) {
+ desc->bNumEndpoints++;
+ }
+ if (tu_edpt_stream_is_opened(&p_midi->ep_stream.rx)) {
+ desc->bNumEndpoints++;
+ }
desc->bInterfaceClass = TUSB_CLASS_AUDIO;
desc->bInterfaceSubClass = AUDIO_SUBCLASS_MIDI_STREAMING;
desc->bInterfaceProtocol = 0;
@@ -558,6 +577,11 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
}
}
}
+ else {
+ // bad packet discard
+ nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
+ 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;
@@ -600,6 +624,11 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff
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];
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index bbfd35a43..3b5d4a855 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_H_
-#define _TUSB_MSC_H_
+#ifndef TUSB_MSC_H_
+#define TUSB_MSC_H_
#include "common/tusb_common.h"
@@ -108,8 +108,7 @@ TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
//--------------------------------------------------------------------+
/// SCSI Command Operation Code
-typedef enum
-{
+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.
@@ -124,8 +123,7 @@ typedef enum
}scsi_cmd_type_t;
/// SCSI Sense Key
-typedef enum
-{
+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.
@@ -141,6 +139,27 @@ typedef enum
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)
//--------------------------------------------------------------------+
@@ -379,4 +398,4 @@ TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
}
#endif
-#endif /* _TUSB_MSC_H_ */
+#endif /* TUSB_MSC_H_ */
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 87c77c9a7..41d9cdfa0 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -42,6 +42,17 @@
#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 {
@@ -53,23 +64,26 @@ enum {
};
typedef struct {
- TU_ATTR_ALIGNED(4) msc_cbw_t cbw;
- TU_ATTR_ALIGNED(4) msc_csw_t csw;
+ 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;
- // Bulk Only Transfer (BOT) Protocol
- uint8_t stage;
-
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
- // Sense Response Data
+ // 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;
+
+ bool pending_io; // pending async IO
}mscd_interface_t;
static mscd_interface_t _mscd_itf;
@@ -78,35 +92,42 @@ CFG_TUD_MEM_SECTION static struct {
TUD_EPBUF_DEF(buf, CFG_TUD_MSC_EP_BUFSIZE);
} _mscd_epbuf;
+TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE >= 64, "CFG_TUD_MSC_EP_BUFSIZE must be at least 64");
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
-
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
-static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
+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(uint8_t rhport, mscd_interface_t* p_msc) {
+TU_ATTR_ALWAYS_INLINE static inline bool send_csw(mscd_interface_t* p_msc) {
// Data residue is always = host expect - actual transferred
+ uint8_t rhport = p_msc->rhport;
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
p_msc->stage = MSC_STAGE_STATUS_SENT;
- memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t));
+ memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t)); //-V1086
return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t));
}
-static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc) {
+TU_ATTR_ALWAYS_INLINE static inline bool prepare_cbw(mscd_interface_t* p_msc) {
+ uint8_t rhport = p_msc->rhport;
p_msc->stage = MSC_STAGE_CMD;
return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t));
}
-static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status) {
+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;
@@ -114,7 +135,7 @@ static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status
// failed but sense key is not set: default to Illegal Request
if (p_msc->sense_key == 0) {
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ (void) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
}
// If there is data stage and not yet complete, stall it
@@ -127,18 +148,18 @@ static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status
}
}
-static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
+TU_ATTR_ALWAYS_INLINE static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
// use offsetof to avoid pointer to the odd/unaligned address
const uint32_t lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
return tu_ntohl(lba); // lba is in Big Endian
}
-static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
return tu_ntohs(block_count);
}
-static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
+TU_ATTR_ALWAYS_INLINE static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
// first extract block count in the command
uint16_t const block_count = rdwr10_get_blockcount(cbw);
if (block_count == 0) {
@@ -152,7 +173,7 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
uint16_t const block_count = rdwr10_get_blockcount(cbw);
if (cbw->total_bytes == 0) {
- if (block_count) {
+ if (block_count > 0) {
TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
} else {
@@ -171,12 +192,88 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
} else if (cbw->total_bytes / block_count == 0) {
TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
+ } else {
+ // nothing to do
}
}
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
//--------------------------------------------------------------------+
@@ -214,15 +311,51 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u
return true;
}
-static inline void set_sense_medium_not_present(uint8_t lun) {
+TU_ATTR_ALWAYS_INLINE static inline void set_sense_medium_not_present(uint8_t lun) {
// default sense is NOT READY, MEDIUM NOT PRESENT
- tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
+ (void) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
+}
+
+static void proc_async_io_done(void *bytes_io) {
+ 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 = false;
+ 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; // nothing to do
+ }
+
+ // 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));
}
@@ -245,12 +378,13 @@ uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
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(rhport, p_msc), drv_len);
+ TU_ASSERT(prepare_cbw(p_msc), drv_len);
return drv_len;
}
@@ -289,16 +423,18 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
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(rhport, p_msc));
+ 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(rhport, p_msc));
+ TU_ASSERT(prepare_cbw(p_msc));
}
}
+ } else {
+ // nothing to do
}
}
@@ -320,11 +456,8 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
TU_LOG_DRV(" MSC Get Max Lun\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
- uint8_t maxlun = 1;
- if (tud_msc_get_maxlun_cb) {
- maxlun = tud_msc_get_maxlun_cb();
- }
- TU_VERIFY(maxlun);
+ uint8_t maxlun = tud_msc_get_maxlun_cb();
+ TU_VERIFY(maxlun != 0);
maxlun--; // MAX LUN is minus 1 by specs
tud_control_xfer(rhport, request, &maxlun, 1);
break;
@@ -382,12 +515,12 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
uint8_t const status = rdwr10_validate_cmd(p_cbw);
if (status != MSC_CSW_STATUS_PASSED) {
- fail_scsi_op(rhport, p_msc, status);
- } else if (p_cbw->total_bytes) {
+ fail_scsi_op(p_msc, status);
+ } else if (p_cbw->total_bytes > 0) {
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
- proc_read10_cmd(rhport, p_msc);
+ proc_read10_cmd(p_msc);
} else {
- proc_write10_cmd(rhport, p_msc);
+ proc_write10_cmd(p_msc);
}
} else {
// no data transfer, only exist in complaint test suite
@@ -400,7 +533,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
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(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ 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
@@ -418,12 +551,12 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
if (resplen < 0) {
// unsupported command
TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
} else if (resplen == 0) {
- if (p_cbw->total_bytes) {
+ if (p_cbw->total_bytes > 0) {
// 6.7 The 13 Cases: case 4 (Hi > Dn)
// TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
} else {
// case 1 Hn = Dn: all good
p_msc->stage = MSC_STAGE_STATUS;
@@ -432,7 +565,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
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(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ 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);
@@ -456,10 +589,10 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}else {
- proc_read10_cmd(rhport, p_msc);
+ proc_read10_cmd(p_msc);
}
} else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) {
- proc_write10_new_data(rhport, p_msc, xferred_bytes);
+ proc_write10_host_data(p_msc, xferred_bytes);
} else {
p_msc->xferred_len += xferred_bytes;
@@ -470,7 +603,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
if ( cb_result < 0 ) {
// unsupported command
TU_LOG_DRV(" SCSI unsupported command\r\n");
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
}else {
// TODO haven't implement this scenario any further yet
}
@@ -491,7 +624,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
case MSC_STAGE_STATUS_SENT:
- // Wait for the Status phase to complete
+ // 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);
@@ -501,55 +634,30 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// if complete_cb() is invoked after queuing the status.
switch (p_cbw->command[0]) {
case SCSI_CMD_READ_10:
- if (tud_msc_read10_complete_cb) {
- tud_msc_read10_complete_cb(p_cbw->lun);
- }
+ tud_msc_read10_complete_cb(p_cbw->lun);
break;
case SCSI_CMD_WRITE_10:
- if (tud_msc_write10_complete_cb) {
- tud_msc_write10_complete_cb(p_cbw->lun);
- }
+ tud_msc_write10_complete_cb(p_cbw->lun);
break;
default:
- if (tud_msc_scsi_complete_cb) {
- tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
- }
+ tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
break;
}
- TU_ASSERT(prepare_cbw(rhport, p_msc));
+ TU_ASSERT(prepare_cbw(p_msc));
} else {
- // Any xfer ended here is consider unknown error, ignore it
+ // Any xfer ended here is considered unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
break;
- default: break;
+ default: break; // nothing to do
}
if (p_msc->stage == MSC_STAGE_STATUS) {
- // 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(rhport, 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(rhport, p_msc);
- }
- #endif
+ TU_ASSERT(proc_stage_status(p_msc));
}
return true;
@@ -581,40 +689,35 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- case SCSI_CMD_START_STOP_UNIT:
+ case SCSI_CMD_START_STOP_UNIT: {
resplen = 0;
+ scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd;
+ if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) {
+ // Failed status response
+ resplen = -1;
- if (tud_msc_start_stop_cb) {
- scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd;
- if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) {
- // Failed status response
- resplen = -1;
-
- // set default sense if not set by callback
- if (p_msc->sense_key == 0) {
- set_sense_medium_not_present(lun);
- }
+ // 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:
+ 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;
- if (tud_msc_prevent_allow_medium_removal_cb) {
- 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);
- }
+ // 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;
@@ -642,12 +745,11 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = sizeof(read_capa10);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) resplen));
}
+ break;
}
- break;
case SCSI_CMD_READ_FORMAT_CAPACITY: {
- scsi_read_format_capacity_data_t read_fmt_capa =
- {
+ scsi_read_format_capacity_data_t read_fmt_capa = {
.list_length = 8,
.block_num = 0,
.descriptor_type = 2, // formatted media
@@ -675,33 +777,28 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
resplen = sizeof(read_fmt_capa);
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
}
+ break;
}
- break;
case SCSI_CMD_INQUIRY: {
- scsi_inquiry_resp_t inquiry_rsp =
- {
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
- };
+ 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;
- // vendor_id, product_id, product_rev is space padded string
- memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
- memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
- memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
-
- tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
-
- resplen = sizeof(inquiry_rsp);
- TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &inquiry_rsp, (size_t) resplen));
+ 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;
}
- break;
case SCSI_CMD_MODE_SENSE_6: {
- scsi_mode_sense6_resp_t mode_resp =
- {
+ scsi_mode_sense6_resp_t mode_resp = {
.data_len = 3,
.medium_type = 0,
.write_protected = false,
@@ -709,21 +806,17 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
.block_descriptor_len = 0 // no block descriptor are included
};
- bool writable = true;
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(lun);
- }
+ 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;
}
- break;
case SCSI_CMD_REQUEST_SENSE: {
- scsi_sense_fixed_resp_t sense_rsp =
- {
+ scsi_sense_fixed_resp_t sense_rsp = {
.response_code = 0x70, // current, fixed format
.valid = 1
};
@@ -737,14 +830,12 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen));
// request sense callback could overwrite the sense data
- if (tud_msc_request_sense_cb) {
- resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
- }
+ resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
// Clear sense data after copy
- tud_msc_set_sense(lun, 0, 0, 0);
+ (void) tud_msc_set_sense(lun, 0, 0, 0);
+ break;
}
- break;
default: resplen = -1;
break;
@@ -753,95 +844,108 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
return resplen;
}
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+static void proc_read10_cmd(mscd_interface_t* p_msc) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
-
- // block size already verified not zero
- uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
-
- // Adjust lba with transferred bytes
+ uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
+ TU_VERIFY(block_sz != 0, );
+ // Adjust lba & offset with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
+ uint32_t const offset = p_msc->xferred_len % block_sz;
// 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);
- // Application can consume smaller bytes
- uint32_t const offset = p_msc->xferred_len % block_sz;
+ p_msc->pending_io = true;
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes);
+ if (nbytes != TUD_MSC_RET_ASYNC) {
+ p_msc->pending_io = false;
+ proc_read_io_data(p_msc, nbytes);
+ }
+}
- if (nbytes < 0) {
- // negative means error -> endpoint is stalled & status in CSW set to failed
- TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n");
+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;
- // set sense
- set_sense_medium_not_present(p_cbw->lun);
+ 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;
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- } else if (nbytes == 0) {
- // zero means not ready -> simulate an transfer complete so that this driver callback will fired again
- dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
- } else {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
+ default: break; // nothing to do
+ }
}
}
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+static void proc_write10_cmd(mscd_interface_t* p_msc) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
- bool writable = true;
-
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(p_cbw->lun);
- }
+ const bool writable = tud_msc_is_writable_cb(p_cbw->lun);
if (!writable) {
// Not writable, complete this SCSI op with error
// Sense = Write protected
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ (void) 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(rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
+ TU_ASSERT(usbd_edpt_xfer(p_msc->rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
}
// process new data arrived from WRITE10
-static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes) {
+static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) {
msc_cbw_t const* p_cbw = &p_msc->cbw;
+ uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero
+ TU_VERIFY(block_sz != 0, );
- // block size already verified not zero
- uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
-
- // Adjust lba with transferred bytes
+ // Adjust lba & offset with transferred bytes
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
-
- // Invoke callback to consume new data
uint32_t const offset = p_msc->xferred_len % block_sz;
- int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
- if (nbytes < 0) {
- // negative means error -> failed this scsi op
- TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n");
+ p_msc->pending_io = true;
+ int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
+ if (nbytes != TUD_MSC_RET_ASYNC) {
+ p_msc->pending_io = false;
+ proc_write_io_data(p_msc, xferred_bytes, nbytes);
+ }
+}
- // update actual byte before failed
- p_msc->xferred_len += xferred_bytes;
+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;
- set_sense_medium_not_present(p_cbw->lun);
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ default: break; // nothing to do
+ }
} else {
if ((uint32_t)nbytes < xferred_bytes) {
- // Application consume less than what we got (including zero)
+ // 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) {
- p_msc->xferred_len += (uint16_t)nbytes;
memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
}
- // simulate a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
- dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
+ // 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;
@@ -851,7 +955,7 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3
p_msc->stage = MSC_STAGE_STATUS;
} else {
// prepare to receive more data from host
- proc_write10_cmd(rhport, p_msc);
+ proc_write10_cmd(p_msc);
}
}
}
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 29acd280a..21e24971b 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_DEVICE_H_
-#define _TUSB_MSC_DEVICE_H_
+#ifndef TUSB_MSC_DEVICE_H_
+#define TUSB_MSC_DEVICE_H_
#include "common/tusb_common.h"
#include "msc.h"
@@ -48,6 +48,13 @@
#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");
//--------------------------------------------------------------------+
@@ -57,44 +64,41 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
// 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 command
-// - Address = lba * BLOCK_SIZE + offset
-// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
-//
-// - Application fill the buffer (up to bufsize) with address contents and return number of read byte. If
-// - read < bufsize : These bytes are transferred first and callback invoked again for remaining data.
-//
-// - read == 0 : Indicate application is not ready yet e.g disk I/O busy.
-// Callback invoked again with the same parameters later on.
-//
-// - read < 0 : Indicate application error e.g invalid address. This request will be STALLed
-// and return failed status in command status wrapper phase.
+/*
+ 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);
-
-// Invoked when received SCSI WRITE10 command
-// - Address = lba * BLOCK_SIZE + offset
-// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
-//
-// - Application write data from buffer to address contents (up to bufsize) and return number of written byte. If
-// - write < bufsize : callback invoked again with remaining data later on.
-//
-// - write == 0 : Indicate application is not ready yet e.g disk I/O busy.
-// Callback invoked again with the same parameters later on.
-//
-// - write < 0 : Indicate application error e.g invalid address. This request will be STALLed
-// and return failed status in command status wrapper phase.
-//
-// TODO change buffer to const uint8_t*
int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
-// Invoked when received SCSI_CMD_INQUIRY
+// 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);
@@ -124,30 +128,30 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
/*------------- Optional callbacks -------------*/
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
-TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
+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
-TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
+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
-TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
+bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
// Invoked when received REQUEST_SENSE
-TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
+int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
// Invoked when Read10 command is complete
-TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
+void tud_msc_read10_complete_cb(uint8_t lun);
// Invoke when Write10 command is complete, can be used to flush flash caching
-TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
+void tud_msc_write10_complete_cb(uint8_t lun);
// Invoked when command in tud_msc_scsi_cb is complete
-TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
+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
-TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
+bool tud_msc_is_writable_cb(uint8_t lun);
//--------------------------------------------------------------------+
// Internal Class Driver API
@@ -163,4 +167,4 @@ bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t ev
}
#endif
-#endif /* _TUSB_MSC_DEVICE_H_ */
+#endif /* TUSB_MSC_DEVICE_H_ */
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index ef0635bbe..daff345c5 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -88,6 +88,17 @@ TU_ATTR_ALWAYS_INLINE static inline msch_epbuf_t* get_epbuf(uint8_t daddr) {
}
//--------------------------------------------------------------------+
+// 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) {
@@ -112,7 +123,10 @@ bool tuh_msc_mounted(uint8_t dev_addr) {
bool tuh_msc_ready(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
- return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
+ TU_VERIFY(p_msc->mounted);
+ const bool epin_busy = usbh_edpt_busy(dev_addr, p_msc->ep_in);
+ const bool epout_busy = usbh_edpt_busy(dev_addr, p_msc->ep_out);
+ return !epin_busy && !epout_busy;
}
//--------------------------------------------------------------------+
@@ -141,7 +155,7 @@ bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
p_msc->stage = MSC_STAGE_CMD;
if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &epbuf->cbw, sizeof(msc_cbw_t))) {
- usbh_edpt_release(daddr, p_msc->ep_out);
+ (void) usbh_edpt_release(daddr, p_msc->ep_out);
return false;
}
@@ -180,7 +194,7 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
.cmd_code = SCSI_CMD_INQUIRY,
.alloc_length = sizeof(scsi_inquiry_resp_t)
};
- memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
@@ -214,7 +228,7 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
.cmd_code = SCSI_CMD_REQUEST_SENSE,
.alloc_length = 18
};
- memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
@@ -236,7 +250,7 @@ bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, u
.lba = tu_htonl(lba),
.block_count = tu_htons(block_count)
};
- memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_read10, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
}
@@ -258,7 +272,7 @@ bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t
.lba = tu_htonl(lba),
.block_count = tu_htons(block_count)
};
- memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
+ memcpy(cbw.command, &cmd_write10, cbw.cmd_len); //-V1086
return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
}
@@ -304,9 +318,7 @@ void msch_close(uint8_t dev_addr) {
// invoke Application Callback
if (p_msc->mounted) {
- if (tuh_msc_umount_cb) {
- tuh_msc_umount_cb(dev_addr);
- }
+ tuh_msc_umount_cb(dev_addr);
}
tu_memclr(p_msc, sizeof(msch_interface_t));
@@ -329,8 +341,7 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
break;
}
-
- TU_ATTR_FALLTHROUGH; // fallthrough to status stage
+ TU_ATTR_FALLTHROUGH; // fallthrough to data stage
case MSC_STAGE_DATA:
// Status stage
@@ -341,20 +352,19 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
case MSC_STAGE_STATUS:
// SCSI op is complete
p_msc->stage = MSC_STAGE_IDLE;
-
- if (p_msc->complete_cb) {
+ if (p_msc->complete_cb != NULL) {
tuh_msc_complete_data_t const cb_data = {
.cbw = cbw,
.csw = csw,
.scsi_data = p_msc->buffer,
.user_arg = p_msc->complete_arg
};
- p_msc->complete_cb(dev_addr, &cb_data);
+ (void) p_msc->complete_cb(dev_addr, &cb_data);
}
break;
- // unknown state
default:
+ // unknown state
break;
}
@@ -492,14 +502,12 @@ static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_dat
// Capacity response field: Block size and Last LBA are both Big-Endian
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf;
- p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
+ p_msc->capacity[cbw->lun].block_count = (uint32_t) (tu_ntohl(resp->last_lba) + 1u);
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
// Mark enumeration is complete
p_msc->mounted = true;
- if (tuh_msc_mount_cb) {
- tuh_msc_mount_cb(dev_addr);
- }
+ tuh_msc_mount_cb(dev_addr);
// notify usbh that driver enumeration is complete
usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 09d777066..b5fd55547 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -109,10 +109,10 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r
//------------- Application Callback -------------//
// Invoked when a device with MassStorage interface is mounted
-TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr);
+void tuh_msc_mount_cb(uint8_t dev_addr);
// Invoked when a device with MassStorage interface is unmounted
-TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
+void tuh_msc_umount_cb(uint8_t dev_addr);
//--------------------------------------------------------------------+
// Internal Class Driver API
diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h
new file mode 100644
index 000000000..236cf98e0
--- /dev/null
+++ b/src/class/mtp/mtp.h
@@ -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] != 0u) {
+ count++;
+ }
+ const uint32_t added_len = 1u + (uint32_t) count * 2u;
+ 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, 2u * (uint32_t) count);
+ p_container->header->len += 2u * count;
+
+ return added_len;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) {
+ const uint8_t len = (uint8_t) (strlen(str) + 1); // include null
+ TU_ASSERT(p_container->header->len + 1 + 2 * len < CFG_TUD_MTP_EP_BUFSIZE, 0);
+ 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 1u;
+ } 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[]) {
+ size_t nchars = *buf++;
+ memcpy(utf16, buf, 2u * nchars);
+ return 1u + 2u * nchars;
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+#endif
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
new file mode 100644
index 000000000..04bde9415
--- /dev/null
+++ b/src/class/mtp/mtp_device.c
@@ -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
diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h
new file mode 100644
index 000000000..a33f1dc08
--- /dev/null
+++ b/src/class/mtp/mtp_device.h
@@ -0,0 +1,169 @@
+/*
+ * 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
+
+ // buffer for data stage
+ uint16_t bufsize;
+ uint8_t* buf;
+
+ const tusb_control_request_t* request;
+
+ uint32_t session_id;
+} tud_mtp_request_cb_data_t;
+
+// Number of supported operations, events, device properties, capture formats, playback formats
+// 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( //-V2586 [MISRA-C-18.7] Flexible array members should not be declared
+ sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS),
+ TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES),
+ TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS)
+) 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
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index a54e6d662..7dff66823 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -35,8 +35,6 @@
#include "net_device.h"
#include "rndis_protocol.h"
-extern void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
-
#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
@@ -81,6 +79,7 @@ typedef struct {
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);
@@ -95,7 +94,11 @@ void netd_report(uint8_t *buf, uint16_t len) {
const uint8_t rhport = 0;
len = tu_min16(len, sizeof(ecm_notify_t));
- TU_VERIFY(usbd_edpt_claim(rhport, _netd_itf.ep_notif), );
+ 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);
}
@@ -181,8 +184,6 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Open endpoint pair for RNDIS
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0);
- tud_network_init_cb();
-
// we are ready to transmit a packet
can_xmit = true;
@@ -196,11 +197,11 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
}
static void ecm_report(bool nc) {
- const ecm_notify_t ecm_notify_nc = {
+ ecm_notify_t ecm_notify_nc = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
- .wValue = 1, /* Connected */
+ .wValue = ecm_link_is_up ? 1 : 0, /* Use current link state */
.wLength = 0,
},
};
@@ -259,7 +260,6 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
- tud_network_init_cb();
can_xmit = true; // we are ready to transmit a packet
tud_network_recv_renew(); // prepare for incoming packets
}
@@ -286,7 +286,10 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
tud_control_xfer(rhport, request, NULL, 0);
- ecm_report(true);
+ // 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) {
@@ -363,9 +366,8 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
if (_netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif)) {
- if (sizeof(tusb_control_request_t) == xferred_bytes) {
- ecm_report(false);
- }
+ // Notification transfer complete - endpoint is now free
+ // Don't automatically send speed change notification after link state changes
}
return true;
@@ -398,4 +400,31 @@ void tud_network_xmit(void *ref, uint16_t arg) {
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 *)&notify, 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
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 0245a87f2..8989fe0b4 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_NCM_H_
-#define _TUSB_NCM_H_
+#ifndef TUSB_NCM_H_
+#define TUSB_NCM_H_
#include "common/tusb_common.h"
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index f9fda0698..ea3c250fe 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -50,10 +50,6 @@
#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-
#include "device/usbd.h"
#include "device/usbd_pvt.h"
@@ -110,6 +106,7 @@ typedef struct {
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)
@@ -218,7 +215,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
.direction = TUSB_DIR_IN
},
.bRequest = CDC_NOTIF_NETWORK_CONNECTION,
- .wValue = 1 /* Connected */,
+ .wValue = ncm_interface.link_is_up ? 1 : 0, /* Dynamic link state */
.wIndex = ncm_interface.itf_num,
.wLength = 0,
},
@@ -232,6 +229,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
ncm_interface.notification_xmit_is_running = true;
} else {
TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
+ ncm_interface.notification_xmit_is_running = false;
}
} // notification_xmit
@@ -755,6 +753,32 @@ static void tud_network_recv_renew_r(uint8_t 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)
@@ -774,6 +798,12 @@ void netd_init(void) {
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
/**
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index 4c9a92f2d..96c03fd61 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_NET_DEVICE_H_
-#define _TUSB_NET_DEVICE_H_
+#ifndef TUSB_NET_DEVICE_H_
+#define TUSB_NET_DEVICE_H_
#include <stdint.h>
#include "class/cdc/cdc.h"
@@ -56,6 +56,13 @@ typedef enum
#endif
//--------------------------------------------------------------------+
+// Implemented by Application
+//--------------------------------------------------------------------+
+#if CFG_TUD_ECM_RNDIS
+extern void rndis_class_set_handler(uint8_t *data, int size);
+#endif
+
+//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
@@ -87,6 +94,11 @@ void tud_network_init_cb(void);
// 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
//--------------------------------------------------------------------+
@@ -102,4 +114,4 @@ void netd_report (uint8_t *buf, uint16_t len);
}
#endif
-#endif /* _TUSB_NET_DEVICE_H_ */
+#endif /* TUSB_NET_DEVICE_H_ */
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index 327de087c..3bf5e1a17 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBTMC_H__
-#define _TUSB_USBTMC_H__
+#ifndef TUSB_USBTMC_H__
+#define TUSB_USBTMC_H__
#include "common/tusb_common.h"
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index abde9679f..3f6bedd4c 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -45,7 +45,7 @@
*/
//Limitations:
-// "vendor-specific" commands are not handled.
+// "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
@@ -76,11 +76,6 @@
#include "usbtmc_device.h"
-#ifdef xDEBUG
-#include "uart_util.h"
-tu_static char logMsg[150];
-#endif
-
// 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.
@@ -88,7 +83,7 @@ tu_static char logMsg[150];
// 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
+ #define CFG_TUD_USBTMC_INT_EP_SIZE 2
#endif
/*
@@ -96,27 +91,26 @@ tu_static char logMsg[150];
* 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
+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_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;
+typedef usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t;
#else
- typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
+typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
#endif
@@ -131,15 +125,15 @@ typedef struct
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)
+ 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 lastBulkOutTag;// used for aborts (mostly)
uint8_t lastBulkInTag; // used for aborts (mostly)
- uint8_t const * devInBuffer; // pointer to application-layer used for transmissions
+ uint8_t const *devInBuffer;// pointer to application-layer used for transmissions
- usbtmc_capabilities_specific_t const * capabilities;
+ usbtmc_capabilities_specific_t const *capabilities;
} usbtmc_interface_state_t;
typedef struct {
@@ -154,43 +148,62 @@ typedef struct {
} usbtmc_epbuf_t;
static usbtmc_interface_state_t usbtmc_state = {
- .itf_id = 0xFF,
+ .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");
+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)
+#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)
-{
+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)
- {
+ if (oldState == expectedState) {
usbtmc_state.state = newState;
- }
- else
- {
+ } else {
ret = false;
}
criticalLeave();
@@ -205,54 +218,54 @@ static bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_en
// 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,
+ const void *data, size_t len,
bool endOfMessage,
- bool usingTermChar)
-{
+ 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)
- {
+ if (usingTermChar) {
TU_ASSERT(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
TU_ASSERT(termCharRequested);
- TU_ASSERT(((uint8_t const*)data)[len-1u] == termChar);
+ 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;
+ usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t *) usbtmc_epbuf.epin;
tu_varclr(hdr);
- hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
+ 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->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 dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ? len : (txBufLen - headerLen);
const size_t packetLen = headerLen + dataLen;
- memcpy((uint8_t*)(usbtmc_epbuf.epin) + headerLen, data, 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);
+ 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));
+ 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)
-{
+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);
@@ -260,51 +273,43 @@ bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
TU_VERIFY(tu_memcpy_s(usbtmc_epbuf.epnotif, CFG_TUD_USBTMC_INT_EP_SIZE, data, len) == 0);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t)len));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t) len));
return true;
}
-void usbtmcd_init_cb(void)
-{
+void usbtmcd_init_cb(void) {
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
#ifndef NDEBUG
-# if CFG_TUD_USBTMC_ENABLE_488
- if (usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger) {
- TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,);
- }
+ #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
- if (usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse) {
- TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,);
- }
+ 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
+#if OSAL_MUTEX_REQUIRED
osal_mutex_delete(usbtmcLock);
- #endif
+#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 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 const *p_desc;
uint8_t found_endpoints = 0;
- TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 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);
+ TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints == 3), 0);
#endif
TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
@@ -316,17 +321,14 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_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) {
+ 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)
- {
+ 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 {
@@ -345,33 +347,29 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
default:
TU_ASSERT(false, 0);
}
- TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 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);
+ p_desc = tu_desc_next(p_desc);
}
- // bulk endpoints are required, but interrupt IN is optional
+// 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_in != 0, 0);
TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
- if (itf_desc->bNumEndpoints == 2)
- {
+ if (itf_desc->bNumEndpoints == 2) {
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
- }
- else if (itf_desc->bNumEndpoints == 3)
- {
+ } 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)
- {
+ #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
#endif
atomicChangeState(STATE_CLOSED, STATE_NAK);
tud_usbtmc_open_cb(itf_desc->iInterface);
@@ -384,30 +382,27 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// 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)
-{
+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;
+ 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));
+ 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();
+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);
@@ -416,35 +411,32 @@ void usbtmcd_reset_cb(uint8_t rhport)
criticalLeave();
}
-static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
-{
- (void)rhport;
+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_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));
+ 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;
+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);
+ 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)
- {
+ if (len >= usbtmc_state.transfer_size_remaining || shortPacket) {
atEnd = true;
TU_VERIFY(atomicChangeState(STATE_RCV, STATE_NAK));
}
@@ -455,8 +447,7 @@ static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t pack
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))
- {
+ 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;
}
@@ -465,10 +456,9 @@ static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t pack
return true;
}
-static bool handle_devMsgIn(void *data, size_t len)
-{
+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;
+ 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;
@@ -481,137 +471,136 @@ static bool handle_devMsgIn(void *data, size_t len)
termChar = msg->TermChar;
#endif
- if(termCharRequested)
+ 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)
-{
+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) {
+ if (usbtmc_state.state == STATE_CLEARING) {
return true; /* I think we can ignore everything here */
}
- if(ep_addr == usbtmc_state.ep_bulk_out)
- {
+ if (ep_addr == usbtmc_state.ep_bulk_out) {
usbtmc_msg_generic_t *msg = NULL;
- switch(usbtmc_state.state)
- {
- case STATE_IDLE:
- {
+ 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);
+ 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:
- if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
- {
- usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
- return false;
- }
- break;
+ 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:
- TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
- 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));
+ 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;
+ break;
#endif
- case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
- case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
- default:
+ 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_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_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;
+ 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;
+ } 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 += sizeof(usbtmc_state.transfer_size_remaining);
- 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 ))
+ 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
{
- usbtmc_state.state = STATE_TX_SHORTED;
+ 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;
+ 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:
+ // 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;
+ 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) {
- if (tud_usbtmc_notification_complete_cb) {
- TU_VERIFY(tud_usbtmc_notification_complete_cb());
+ default:
+ TU_ASSERT(false);
}
+ } else if (ep_addr == usbtmc_state.ep_int_in) {
+ TU_VERIFY(tud_usbtmc_notification_complete_cb());
return true;
}
return false;
@@ -620,188 +609,157 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
// 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)
-{
+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;
+ 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) &&
+ 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))
- {
+ (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)
- {
+ 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
+ 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);
+ } 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))
- {
+ } 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
- {
+ 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)
- {
+ 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);
+ 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;
+ // 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;
}
- else if(usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu))
- {
- rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
+
+ 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;
}
- 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);
+
+ 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;
}
- 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_CHECK_ABORT_BULK_IN_STATUS: {
+ TU_VERIFY(request->bmRequestType == 0xA2);// in,class,EP
+ TU_VERIFY(request->wLength == 8u);
- 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
+ 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();
- 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;
+ 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;
}
- 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);
+ criticalLeave();
+ TU_VERIFY(tud_control_xfer(rhport, request, (void *) &rsp, sizeof(rsp)));
- 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;
+ return true;
}
- 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
+ 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.
@@ -811,112 +769,97 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
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)));
+ 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
+ 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))
- {
+ 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
- {
+ } else {
// Let app check if it's clear
TU_VERIFY(tud_usbtmc_check_clear_cb(&clearStatusRsp));
}
- if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
- {
+ 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)));
+ 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
+ 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)));
+ TU_VERIFY(tud_control_xfer(rhport, request, (void *) (uintptr_t) usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
return true;
}
- // USBTMC Optional Requests
+ // USBTMC Optional Requests
- case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
+ case USBTMC_bREQUEST_INDICATOR_PULSE:// Optional
{
- TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
+ 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)));
+ 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:
- {
+ // 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
+ 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((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.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
- {
+ } 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))
- };
+ {
+ .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));
+ usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void *) &intMsg, sizeof(intMsg));
}
- }
- else
- {
+ } else {
rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
}
- TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
+ 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
+ // 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;
+ default:
+ return false;
}
}
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index b85ef12b5..235f1276e 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -75,15 +75,14 @@ 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.
-TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void);
+bool tud_usbtmc_notification_complete_cb(void);
// Indicator pulse should be 0.5 to 1.0 seconds long
-TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
+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);
-TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
-//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
+bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
#endif
// Called from app
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 2fc0ac944..7da4d2239 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -67,6 +67,20 @@ typedef struct {
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
//--------------------------------------------------------------------
@@ -188,48 +202,52 @@ void vendord_reset(uint8_t rhport) {
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_clear(&p_itf->tx.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);
- const uint8_t* desc_end = (uint8_t const*)desc_itf + max_len;
// Find available interface
vendord_interface_t* p_vendor = NULL;
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
- if (!tud_vendor_n_mounted(i)) {
- p_vendor = &_vendord_itf[i];
+ 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;
- uint8_t found_ep = 0;
- while (found_ep < desc_itf->bNumEndpoints) {
- // skip non-endpoint descriptors
- while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) ) {
- p_desc = tu_desc_next(p_desc);
- }
- if (p_desc >= desc_end) {
- break;
- }
-
- const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
- TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
- found_ep++;
+ 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));
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
- tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
- tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
- } else {
- 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
+ // open endpoint stream, skip if already opened (multiple IN/OUT endpoints)
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_t *stream_tx = &p_vendor->tx.stream;
+ if (stream_tx->ep_addr == 0) {
+ tu_edpt_stream_open(stream_tx, desc_ep);
+ tu_edpt_stream_write_xfer(rhport, stream_tx); // flush pending data
+ }
+ } else {
+ tu_edpt_stream_t *stream_rx = &p_vendor->rx.stream;
+ if (stream_rx->ep_addr == 0) {
+ tu_edpt_stream_open(stream_rx, desc_ep);
+ TU_ASSERT(tu_edpt_stream_read_xfer(rhport, stream_rx) > 0, 0); // prepare for incoming data
+ }
+ }
}
p_desc = tu_desc_next(p_desc);
@@ -258,16 +276,12 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes);
// Invoked callback if any
- if (tud_vendor_rx_cb) {
- tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes);
- }
+ 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
- if (tud_vendor_tx_cb) {
- tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
- }
+ tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
// try to send more if possible
diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h
index 149ae2d56..5376f3917 100644
--- a/src/class/vendor/vendor_device.h
+++ b/src/class/vendor/vendor_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_VENDOR_DEVICE_H_
-#define _TUSB_VENDOR_DEVICE_H_
+#ifndef TUSB_VENDOR_DEVICE_H_
+#define TUSB_VENDOR_DEVICE_H_
#include "common/tusb_common.h"
@@ -119,9 +119,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
//--------------------------------------------------------------------+
// Invoked when received new data
-TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
+void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
// Invoked when last rx transfer finished
-TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
+void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
//--------------------------------------------------------------------+
// Inline Functions
@@ -141,4 +141,4 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, u
}
#endif
-#endif /* _TUSB_VENDOR_DEVICE_H_ */
+#endif /* TUSB_VENDOR_DEVICE_H_ */
diff --git a/src/class/vendor/vendor_host.h b/src/class/vendor/vendor_host.h
index acfebe7a4..00e3c3402 100644
--- a/src/class/vendor/vendor_host.h
+++ b/src/class/vendor/vendor_host.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_VENDOR_HOST_H_
-#define _TUSB_VENDOR_HOST_H_
+#ifndef TUSB_VENDOR_HOST_H_
+#define TUSB_VENDOR_HOST_H_
#include "common/tusb_common.h"
@@ -64,4 +64,4 @@ void cush_close(uint8_t dev_addr);
}
#endif
-#endif /* _TUSB_VENDOR_HOST_H_ */
+#endif /* TUSB_VENDOR_HOST_H_ */
diff --git a/src/class/video/video.h b/src/class/video/video.h
index f348e187b..5bdf4b840 100644
--- a/src/class/video/video.h
+++ b/src/class/video/video.h
@@ -219,11 +219,11 @@ typedef enum {
uint8_t baInterfaceNr[_nitf]; \
}
-typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
-typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
-typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
-typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
-typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
+typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t; //-V2586 incorrectly detected as flexible array
+typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t; //-V2586 incorrectly detected as flexible array
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index 3124d5596..844a5e672 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -103,7 +103,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t index_vc; /* index of bound video control interface */
uint8_t index_vs; /* index from the video control interface */
struct {
- uint16_t beg; /* Offset of the begging of video streaming interface descriptor */
+ uint16_t beg; /* Offset of the beginning of video streaming interface descriptor */
uint16_t end; /* Offset of the end of video streaming interface descriptor */
uint16_t cur; /* Offset of the current settings */
uint16_t ep[2]; /* Offset of endpoint descriptors. 0: streaming, 1: still capture */
@@ -193,6 +193,34 @@ static char const* const tu_str_video_vs_control_selector[] = {
#endif
//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
+ (void) ctl_idx;
+ (void) stm_idx;
+}
+
+TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod) {
+ (void) ctl_idx;
+ (void) power_mod;
+ return VIDEO_ERROR_NONE;
+}
+
+TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
+ video_probe_and_commit_control_t const *parameters) {
+ (void) ctl_idx;
+ (void) stm_idx;
+ (void) parameters;
+ return VIDEO_ERROR_NONE;
+}
+
+TU_ATTR_WEAK void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request) {
+ (void) ctl_idx;
+ (void) stm_idx;
+ (void) request;
+}
+
+//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -222,9 +250,13 @@ static inline uint8_t _desc_ep_addr(void const *desc) {
* @return instance */
static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
videod_interface_t *ctl = &_videod_itf[ctl_idx];
- if (!ctl->beg) return NULL;
+ if (!ctl->beg) {
+ return NULL;
+ }
videod_streaming_interface_t *stm = &_videod_streaming_itf[ctl->stm[stm_idx]];
- if (!stm->desc.beg) return NULL;
+ if (!stm->desc.beg) {
+ return NULL;
+ }
return stm;
}
@@ -233,7 +265,9 @@ static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self) {
}
static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self) {
- if (!self->desc.cur) return NULL;
+ if (!self->desc.cur) {
+ return NULL;
+ }
uint8_t const *desc = _videod_itf[self->index_vc].beg;
return (tusb_desc_vs_itf_t const*)(desc + self->desc.cur);
}
@@ -344,8 +378,12 @@ static void const* _find_desc_ep(void const *beg, void const *end)
{
for (void const *cur = beg; cur < end; cur = tu_desc_next(cur)) {
uint_fast8_t desc_type = tu_desc_type(cur);
- if (TUSB_DESC_ENDPOINT == desc_type) return cur;
- if (TUSB_DESC_INTERFACE == desc_type) break;
+ if (TUSB_DESC_ENDPOINT == desc_type) {
+ return cur;
+ }
+ if (TUSB_DESC_INTERFACE == desc_type) {
+ break;
+ }
}
return end;
}
@@ -431,8 +469,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
uint_fast8_t fmtnum = param->bFormatIndex;
TU_ASSERT(vs && fmtnum <= vs->stm.bNumFormats);
- if (!fmtnum) {
- if (1 < vs->stm.bNumFormats) return true; /* Need to negotiate all variables. */
+ if (0 == fmtnum) {
+ if (1 < vs->stm.bNumFormats) {
+ return true; /* Need to negotiate all variables. */
+ }
fmtnum = 1;
param->bFormatIndex = 1;
}
@@ -470,8 +510,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
uint_fast8_t frmnum = param->bFrameIndex;
TU_ASSERT(frmnum <= fmt->bNumFrameDescriptors);
- if (!frmnum) {
- if (1 < fmt->bNumFrameDescriptors) return true;
+ if (0 == frmnum) {
+ if (1 < fmt->bNumFrameDescriptors) {
+ return true;
+ }
frmnum = 1;
param->bFrameIndex = 1;
}
@@ -480,7 +522,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
/* Set the parameters determined by the frame */
uint_fast32_t frame_size = param->dwMaxVideoFrameSize;
- if (!frame_size) {
+ if (0 == frame_size) {
switch (fmt->bDescriptorSubType) {
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8;
@@ -500,7 +542,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
}
uint_fast32_t interval = param->dwFrameInterval;
- if (!interval) {
+ if (0 == interval) {
if ((1 < frm->uncompressed.bFrameIntervalType) ||
((0 == frm->uncompressed.bFrameIntervalType) &&
(frm->uncompressed.dwFrameInterval[1] != frm->uncompressed.dwFrameInterval[0]))) {
@@ -510,7 +552,7 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
param->dwFrameInterval = interval;
}
uint_fast32_t interval_ms = interval / 10000;
- TU_ASSERT(interval_ms);
+ TU_ASSERT(interval_ms != 0);
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
@@ -528,7 +570,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
video_probe_and_commit_control_t *param)
{
uint_fast8_t const fmtnum = param->bFormatIndex;
- if (!fmtnum) {
+ if (0 == fmtnum) {
switch (request) {
case VIDEO_REQUEST_GET_MAX:
if (_get_desc_vs(stm))
@@ -559,7 +601,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
}
uint_fast8_t frmnum = param->bFrameIndex;
- if (!frmnum) {
+ if (0 == frmnum) {
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
TU_ASSERT(vs);
void const *end = _end_of_streaming_descriptor(vs);
@@ -615,7 +657,7 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
return true;
}
- if (!param->dwFrameInterval) {
+ if (0 == param->dwFrameInterval) {
tusb_desc_vs_itf_t const *vs = _get_desc_vs(stm);
TU_ASSERT(vs);
void const *end = _end_of_streaming_descriptor(vs);
@@ -664,12 +706,12 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
default: return false;
}
param->dwFrameInterval = interval;
- if (!interval) {
+ if (0 == interval) {
param->dwMaxPayloadTransferSize = 0;
} else {
uint_fast32_t frame_size = param->dwMaxVideoFrameSize;
uint_fast32_t payload_size;
- if (!interval_ms) {
+ if (0 == interval_ms) {
payload_size = frame_size + 2;
} else {
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
@@ -697,7 +739,7 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self)
/* The end of the video control interface descriptor. */
void const *end = _end_of_control_descriptor(vc);
- if (vc->std.bNumEndpoints) {
+ if (vc->std.bNumEndpoints != 0) {
/* Find the notification endpoint descriptor. */
cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT);
TU_ASSERT(cur < end);
@@ -735,7 +777,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
cur += vc->std.bLength + vc->ctl.bLength;
TU_LOG_DRV(" bNumEndpoints %d\r\n", vc->std.bNumEndpoints);
/* Open the notification endpoint if it exist. */
- if (vc->std.bNumEndpoints) {
+ if (vc->std.bNumEndpoints != 0) {
/* Support for 1 endpoint only. */
TU_VERIFY(1 == vc->std.bNumEndpoints);
/* Find the notification endpoint descriptor. */
@@ -821,7 +863,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
stm->desc.ep[i] = (uint16_t) (cur - desc);
TU_LOG_DRV(" open EP%02x\r\n", _desc_ep_addr(cur));
}
- if (altnum) {
+ if (altnum != 0) {
stm->state = VS_STATE_STREAMING;
}
TU_LOG_DRV(" done\r\n");
@@ -838,7 +880,16 @@ static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm, uint
}
TU_ASSERT(pkt_len >= hdr_len);
uint_fast16_t data_len = pkt_len - hdr_len;
- memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ if (stm->buffer) {
+ memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ } else {
+ tud_video_payload_request_t rqst = {
+ .buf = &ep_buf[hdr_len],
+ .length = data_len,
+ .offset = stm->offset
+ };
+ tud_video_prepare_payload_cb(stm->index_vc, stm->index_vs, &rqst);
+ }
stm->offset += data_len;
remaining -= data_len;
if (!remaining) {
@@ -902,21 +953,19 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode)), VIDEO_ERROR_UNKNOWN);
} else if (stage == CONTROL_STAGE_DATA) {
- if (tud_video_power_mode_cb) return tud_video_power_mode_cb(ctl_idx, self->power_mode);
+ return tud_video_power_mode_cb(ctl_idx, self->power_mode);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN);
}
@@ -929,15 +978,13 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_VC_CTL_REQUEST_ERROR_CODE:
switch (request->bRequest) {
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(tud_control_xfer(rhport, request, &self->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get, sizeof(_cap_get)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -964,7 +1011,7 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
case TUSB_REQ_TYPE_CLASS: {
uint_fast8_t entity_id = TU_U16_HIGH(request->wIndex);
- if (!entity_id) {
+ if (0 == entity_id) {
return handle_video_ctl_cs_req(rhport, stage, request, ctl_idx);
} else {
TU_VERIFY(_find_desc_entity(_get_desc_vc(&_videod_itf[ctl_idx]), entity_id), VIDEO_ERROR_INVALID_REQUEST);
@@ -979,14 +1026,12 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
- uint_fast8_t stm_idx)
-{
+ uint_fast8_t stm_idx) {
TU_LOG_DRV("\r\n");
videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
switch (request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
tusb_desc_vs_itf_t const *vs = _get_desc_vs(self);
TU_VERIFY(vs, VIDEO_ERROR_UNKNOWN);
@@ -1053,12 +1098,14 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
} else if (stage == CONTROL_STAGE_DATA) {
TU_VERIFY(_update_streaming_parameters(stm, &stm->probe_commit_payload),
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ } else {
+ // nothing to do
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1068,7 +1115,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_RES:
case VIDEO_REQUEST_GET_DEF:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
video_probe_and_commit_control_t tmp = stm->probe_commit_payload;
TU_VERIFY(_negotiate_streaming_parameters(stm, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN);
@@ -1104,10 +1151,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
TU_VERIFY(_update_streaming_parameters(stm, param), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
/* Set the negotiated value */
stm->max_payload_transfer_size = param->dwMaxPayloadTransferSize;
- int ret = VIDEO_ERROR_NONE;
- if (tud_video_commit_cb) {
- ret = tud_video_commit_cb(stm->index_vc, stm->index_vs, param);
- }
+ int ret = tud_video_commit_cb(stm->index_vc, stm->index_vs, param);
if (VIDEO_ERROR_NONE == ret) {
stm->state = VS_STATE_COMMITTED;
stm->buffer = NULL;
@@ -1118,12 +1162,14 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
hdr->bHeaderLength = sizeof(*hdr);
hdr->bmHeaderInfo = 0;
}
+ } else {
+ // nothing to do
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(request->wLength != 0, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -1166,14 +1212,15 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
static int handle_video_stm_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
- uint_fast8_t stm_idx)
-{
+ uint_fast8_t stm_idx) {
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
return handle_video_stm_std_req(rhport, stage, request, stm_idx);
case TUSB_REQ_TYPE_CLASS:
- if (TU_U16_HIGH(request->wIndex)) return VIDEO_ERROR_INVALID_REQUEST;
+ if (0 != TU_U16_HIGH(request->wIndex)) {
+ return VIDEO_ERROR_INVALID_REQUEST;
+ }
return handle_video_stm_cs_req(rhport, stage, request, stm_idx);
default: return VIDEO_ERROR_INVALID_REQUEST;
@@ -1184,11 +1231,12 @@ static int handle_video_stm_req(uint8_t rhport, uint8_t stage,
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_video_n_connected(uint_fast8_t ctl_idx)
-{
+bool tud_video_n_connected(uint_fast8_t ctl_idx) {
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, 0);
- if (stm) return true;
+ if (stm != NULL) {
+ return true;
+ }
return false;
}
@@ -1197,15 +1245,21 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
- if (!stm || !stm->desc.ep[0]) return false;
- if (stm->state == VS_STATE_PROBING) return false;
+ if (NULL == stm || 0 == stm->desc.ep[0]) {
+ return false;
+ }
+ if (stm->state == VS_STATE_PROBING) {
+ return false;
+ }
-#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ #ifdef TUP_DCD_EDPT_ISO_ALLOC
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
uint_fast16_t ofs_ep = stm->desc.ep[0];
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- if (stm->state == VS_STATE_COMMITTED) return false;
+ if (stm->state == VS_STATE_COMMITTED) {
+ return false;
+ }
}
#endif
@@ -1216,25 +1270,36 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
- if (!buffer || !bufsize) return false;
+ if (0 == bufsize) {
+ return false;
+ }
+
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[ctl_idx];
- if (!stm || !stm->desc.ep[0] || stm->buffer) return false;
- if (stm->state == VS_STATE_PROBING) return false;
+ if (NULL == stm || 0 == stm->desc.ep[0] || stm->bufsize) {
+ return false;
+ }
+ if (stm->state == VS_STATE_PROBING) {
+ return false;
+ }
/* Find EP address */
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
uint8_t ep_addr = 0;
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
uint_fast16_t ofs_ep = stm->desc.ep[i];
- if (!ofs_ep) continue;
+ if (0 == ofs_ep) {
+ continue;
+ }
ep_addr = _desc_ep_addr(desc + ofs_ep);
break;
}
- if (!ep_addr) return false;
+ if (0 == ep_addr) {
+ return false;
+ }
- TU_VERIFY( usbd_edpt_claim(0, ep_addr) );
+ TU_VERIFY(usbd_edpt_claim(0, ep_addr));
/* update the packet header */
tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
hdr->FrameID ^= 1;
@@ -1286,7 +1351,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
videod_interface_t *self = NULL;
uint8_t ctl_idx;
for (ctl_idx = 0; ctl_idx < CFG_TUD_VIDEO; ++ctl_idx) {
- if (_videod_itf[ctl_idx].beg) continue;
+ if (NULL != _videod_itf[ctl_idx].beg) {
+ continue;
+ }
self = &_videod_itf[ctl_idx];
break;
}
@@ -1307,7 +1374,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
videod_streaming_interface_t *stm = NULL;
/* find free streaming interface handle */
for (uint8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
- if (_videod_streaming_itf[i].desc.beg) continue;
+ if (0 != _videod_streaming_itf[i].desc.beg) {
+ continue;
+ }
stm = &_videod_streaming_itf[i];
self->stm[stm_idx] = i;
break;
@@ -1335,7 +1404,9 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
}
p_desc = tu_desc_next(p_desc);
}
- if(ep_addr > 0 && ep_size > 0) usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
+ if(ep_addr > 0 && ep_size > 0) {
+ usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
+ }
#endif
if (0 == stm_idx && 1 == bInCollection) {
/* If there is only one streaming interface and no alternate settings,
@@ -1362,31 +1433,43 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
uint_fast8_t itf;
for (itf = 0; itf < CFG_TUD_VIDEO; ++itf) {
void const *desc = _videod_itf[itf].beg;
- if (!desc) continue;
- if (itfnum == _desc_itfnum(desc)) break;
+ if (!desc) {
+ continue;
+ }
+ if (itfnum == _desc_itfnum(desc)) {
+ break;
+ }
}
if (itf < CFG_TUD_VIDEO) {
TU_LOG_DRV(" VC[%d]: ", itf);
err = handle_video_ctl_req(rhport, stage, request, itf);
_videod_itf[itf].error_code = (uint8_t)err;
- if (err) return false;
+ if (0 != err) {
+ return false;
+ }
return true;
}
/* Identify which streaming interface to use */
for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[itf];
- if (!stm->desc.beg) continue;
+ if (0 == stm->desc.beg) {
+ continue;
+ }
uint8_t const *desc = _videod_itf[stm->index_vc].beg;
- if (itfnum == _desc_itfnum(desc + stm->desc.beg)) break;
+ if (itfnum == _desc_itfnum(desc + stm->desc.beg)) {
+ break;
+ }
}
if (itf < CFG_TUD_VIDEO_STREAMING) {
TU_LOG_DRV(" VS[%d]: ", itf);
err = handle_video_stm_req(rhport, stage, request, itf);
_videod_streaming_itf[itf].error_code = (uint8_t)err;
- if (err) return false;
+ if (err != 0) {
+ return false;
+ }
return true;
}
return false;
@@ -1402,26 +1485,28 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) {
stm = &_videod_streaming_itf[itf];
uint_fast16_t const ep_ofs = stm->desc.ep[0];
- if (!ep_ofs) continue;
+ if (0 == ep_ofs) {
+ continue;
+ }
ctl = &_videod_itf[stm->index_vc];
uint8_t const *desc = ctl->beg;
- if (ep_addr == _desc_ep_addr(desc + ep_ofs)) break;
+ if (ep_addr == _desc_ep_addr(desc + ep_ofs)) {
+ break;
+ }
}
TU_ASSERT(itf < CFG_TUD_VIDEO_STREAMING);
videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[itf];
if (stm->offset < stm->bufsize) {
/* Claim the endpoint */
- TU_VERIFY( usbd_edpt_claim(rhport, ep_addr), 0);
+ TU_VERIFY(usbd_edpt_claim(rhport, ep_addr), 0);
uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
- TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
} else {
stm->buffer = NULL;
stm->bufsize = 0;
stm->offset = 0;
- if (tud_video_frame_xfer_complete_cb) {
- tud_video_frame_xfer_complete_cb(stm->index_vc, stm->index_vs);
- }
+ tud_video_frame_xfer_complete_cb(stm->index_vc, stm->index_vs);
}
return true;
}
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index 648a221d5..f14555e4f 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -35,6 +35,16 @@
extern "C" {
#endif
+
+//--------------------------------------------------------------------+
+// Payload request
+//--------------------------------------------------------------------+
+typedef struct TU_ATTR_PACKED {
+ void* buf; /* Payload buffer to be filled */
+ size_t length; /* Length of the requested data in bytes */
+ size_t offset; /* Offset within the frame (in bytes) */
+} tud_video_payload_request_t;
+
//--------------------------------------------------------------------+
// Application API (Multiple Ports)
// CFG_TUD_VIDEO > 1
@@ -61,7 +71,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
*
* @param[in] ctl_idx Destination control interface index
* @param[in] stm_idx Destination streaming interface index */
-TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
+void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
@@ -72,7 +82,7 @@ TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fa
* @param[in] ctl_idx Destination control interface index
* @param[in] stm_idx Destination streaming interface index
* @return video_error_code_t */
-TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
+int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
/** Invoked when VS_COMMIT_CONTROL(SET_CUR) request received
*
@@ -80,9 +90,19 @@ TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod
* @param[in] stm_idx Destination streaming interface index
* @param[in] parameters Video streaming parameters
* @return video_error_code_t */
-TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
+int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
video_probe_and_commit_control_t const *parameters);
+/** Invoked if buffer is set to NULL (allows bufferless on the fly data generation)
+ *
+ * @param[in] ctl_idx Destination control interface index
+ * @param[in] stm_idx Destination streaming interface index
+ * @param[out] payload_buf Payload storage buffer (target buffer for requested data)
+ * @param[in] payload_size Size of payload_buf (requested data size)
+ * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize)
+ * @return video_error_code_t */
+void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request);
+
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index 0351a3d8f..f377d5272 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_COMMON_H_
-#define _TUSB_COMMON_H_
+#ifndef TUSB_COMMON_H_
+#define TUSB_COMMON_H_
#ifdef __cplusplus
extern "C" {
@@ -34,29 +34,38 @@
//--------------------------------------------------------------------+
// Macros Helper
//--------------------------------------------------------------------+
-#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
-#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_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
+#define TU_FIELD_SIZE(_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_DIV_ROUND_NEAREST(v, d) (((v) + (d)/2) / (d) ) // round to nearest integer
-#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_U16(_high, _low) ((uint16_t) ((((uint16_t) (_high)) << 8) | ((uint16_t) (_low))))
+#define TU_U16_HIGH(_u16) ((uint8_t) (((uint16_t) (_u16) >> 8) & 0x00ffu))
+#define TU_U16_LOW(_u16) ((uint8_t) ((uint16_t) (_u16) & 0x00ffu))
+#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 TU_U24(_high, _mid, _low) ((uint32_t) ((((uint32_t) (_high)) << 16) | (((uint32_t) (_mid)) << 8) | ((uint32_t) (_low))))
+#define TU_U24_HIGH(_u24) ((uint8_t) (((uint32_t) (_u24) >> 16) & 0x0000ffu))
+#define TU_U24_MID(_u24) ((uint8_t) (((uint32_t) (_u24) >> 8) & 0x0000ffu))
+#define TU_U24_LOW(_u24) ((uint8_t) ((uint32_t) (_u24) & 0x0000ffu))
+#define U24_TO_U8S_BE(_u24) TU_U24_HIGH(_u24), TU_U24_MID(_u24), TU_U24_LOW(_u24)
+#define U24_TO_U8S_LE(_u24) TU_U24_LOW(_u24), TU_U24_MID(_u24), TU_U24_HIGH(_u24)
-#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_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 TU_BIT(n) (1UL << (n))
+#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))) )
+#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
//--------------------------------------------------------------------+
// Includes
@@ -68,7 +77,6 @@
#include <inttypes.h>
#include <stddef.h>
#include <string.h>
-#include <stdio.h>
// Tinyusb Common Headers
#include "tusb_option.h"
@@ -78,45 +86,72 @@
#include "tusb_debug.h"
//--------------------------------------------------------------------+
-// Optional API implemented by application if needed
+// 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
-TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);
+extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);
// invalidate data cache
-TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);
+extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);
// Optional physical <-> virtual address translation
-TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr);
-TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr);
+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_memclr(buffer, size) (void) 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) {
- // TODO may check if desst and src is not NULL
- if ( count > destsz ) {
+ // Validate parameters
+ if (dest == NULL) {
return -1;
}
- memset(dest, ch, count);
+
+ if (count == 0u) {
+ return 0;
+ }
+
+ if (count > destsz) {
+ return -1;
+ }
+
+ (void) 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) {
- // TODO may check if desst and src is not NULL
- if ( count > destsz ) {
+ if (dest == NULL) {
return -1;
}
- memcpy(dest, src, count);
+
+ if (count == 0u) {
+ return 0;
+ }
+
+ if (src == NULL) {
+ return -1;
+ }
+
+ if (count > destsz) {
+ return -1;
+ }
+
+ (void) memcpy(dest, src, count);
return 0;
}
@@ -162,8 +197,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return
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 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 -------------//
@@ -193,13 +228,17 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { retur
//------------- 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_div_round_nearest(uint32_t v, uint32_t d) { return TU_DIV_ROUND_NEAREST(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++; }
+ while ((value >>= 1u) != 0u) {
+ result++;
+ }
return result;
}
@@ -245,14 +284,12 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_
// 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)
-{
+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)
-{
+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);
@@ -260,20 +297,17 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_
buf8[3] = tu_u32_byte3(value);
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
-{
+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)
-{
+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
@@ -295,37 +329,49 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_
#endif
-// To be removed
-//------------- Binary constant -------------//
-#if defined(__GNUC__) && !defined(__CC_ARM)
+//--------------------------------------------------------------------+
+// Descriptor helper
+//--------------------------------------------------------------------+
-#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))
+// 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];
+}
-#else
+// 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];
+}
-// 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))
+// 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];
+}
-#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
+// 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 bool tu_desc_in_bounds(const uint8_t *p_desc, const uint8_t *desc_end) {
+ if (p_desc >= desc_end) {
+ return false;
+ }
+ return 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_ */
+#endif /* TUSB_COMMON_H_ */
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 9b33a6f61..7719790d1 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -29,8 +29,8 @@
* \brief Group_Compiler brief
* @{ */
-#ifndef _TUSB_COMPILER_H_
-#define _TUSB_COMPILER_H_
+#ifndef TUSB_COMPILER_H_
+#define TUSB_COMPILER_H_
#define TU_TOKEN(x) x
#define TU_STRING(x) #x ///< stringify without expand
@@ -45,9 +45,9 @@
#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) )
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
- #define _TU_COUNTER_ __COUNTER__
+ #define TU_COUNTER __COUNTER__
#else
- #define _TU_COUNTER_ __LINE__
+ #define TU_COUNTER __LINE__
#endif
// Compile-time Assert
@@ -56,9 +56,9 @@
#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];
+ #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)) }
+ #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, TU_COUNTER) = 1/(!!(const_expr)) }
#endif
/* --------------------- Fuzzing types -------------------------------------- */
@@ -68,28 +68,28 @@
#define tu_static static
#endif
-// for declaration of reserved field, make use of _TU_COUNTER_
-#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
+// 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
+ * - reference www.stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments
+ * - TU_GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
+ * - TU_NARG_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())
+#if defined(__CCRX__)
+#define TU_ARGS_NUM(...) TU_NARG_IMPL(_0, __VA_ARGS__, TU_NARG_RSEQ_N())
#else
-#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N())
+#define TU_ARGS_NUM(...) TU_NARG_IMPL(_0, ##__VA_ARGS__, TU_NARG_RSEQ_N())
#endif
-#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__)
-#define _GET_NTH_ARG( \
+#define TU_NARG_IMPL(...) TU_GET_NTH_ARG(__VA_ARGS__)
+#define TU_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, \
@@ -97,7 +97,7 @@
_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() \
+#define TU_NARG_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, \
@@ -106,17 +106,29 @@
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__)
+// Apply a macro X to each of the arguments with a separation/delimiter
+#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)
+#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)
+
+// Apply a macro X to each of the arguments and expand the result with comma
+#define TU_ARGS_APPLY_EXPAND(_X, ...) TU_XSTRCAT(TU_ARGS_APPLY_EXPAND_, TU_ARGS_NUM(__VA_ARGS__))(_X, __VA_ARGS__)
+
+#define TU_ARGS_APPLY_EXPAND_1(_X, _a1) _X(_a1)
+#define TU_ARGS_APPLY_EXPAND_2(_X, _a1, _a2) _X(_a1), _X(_a2)
+#define TU_ARGS_APPLY_EXPAND_3(_X, _a1, _a2, _a3) _X(_a1), TU_ARGS_APPLY_EXPAND_2(_X, _a2, _a3)
+#define TU_ARGS_APPLY_EXPAND_4(_X, _a1, _a2, _a3, _a4) _X(_a1), TU_ARGS_APPLY_EXPAND_3(_X, _a2, _a3, _a4)
+#define TU_ARGS_APPLY_EXPAND_5(_X, _a1, _a2, _a3, _a4, _a5) _X(_a1), TU_ARGS_APPLY_EXPAND_4(_X, _a2, _a3, _a4, _a5)
+#define TU_ARGS_APPLY_EXPAND_6(_X, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1), TU_ARGS_APPLY_EXPAND_5(_X, _a2, _a3, _a4, _a5, _a6)
+#define TU_ARGS_APPLY_EXPAND_7(_X, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1), TU_ARGS_APPLY_EXPAND_6(_X, _a2, _a3, _a4, _a5, _a6, _a7)
+#define TU_ARGS_APPLY_EXPAND_8(_X, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1), TU_ARGS_APPLY_EXPAND_7(_X, _a2, _a3, _a4, _a5, _a6, _a7, _a8)
//--------------------------------------------------------------------+
// Macro for function default arguments
@@ -305,6 +317,6 @@
#error Byte order is undefined
#endif
-#endif /* _TUSB_COMPILER_H_ */
+#endif /* TUSB_COMPILER_H_ */
/// @}
diff --git a/src/common/tusb_debug.h b/src/common/tusb_debug.h
index 1d0c6f1ad..e0e09f5ce 100644
--- a/src/common/tusb_debug.h
+++ b/src/common/tusb_debug.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DEBUG_H_
-#define _TUSB_DEBUG_H_
+#ifndef TUSB_DEBUG_H_
+#define TUSB_DEBUG_H_
#ifdef __cplusplus
extern "C" {
@@ -55,11 +55,14 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
#define tu_printf CFG_TUSB_DEBUG_PRINTF
#else
- #define tu_printf printf
+ #include <stdio.h>
+ #define tu_printf(...) (void) printf(__VA_ARGS__)
#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_ATTR_ALWAYS_INLINE 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");
}
@@ -109,12 +112,16 @@ typedef struct {
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; }
+ 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);
+ if (snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key) <= 0) {
+ not_found[0] = 0;
+ }
return not_found;
}
@@ -130,8 +137,6 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
#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(...)
@@ -166,4 +171,4 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
}
#endif
-#endif /* _TUSB_DEBUG_H_ */
+#endif
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index ecf002b09..5c9e586fb 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -28,7 +28,7 @@
#include "osal/osal.h"
#include "tusb_fifo.h"
-#define TU_FIFO_DBG 0
+#define TU_FIFO_DBG 0
// Suppress IAR warning
// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
@@ -38,14 +38,16 @@
#if OSAL_MUTEX_REQUIRED
-TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex)
-{
- if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex) {
+ if (mutex != NULL) {
+ osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
-{
- if (mutex) osal_mutex_unlock(mutex);
+TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex) {
+ if (mutex != NULL) {
+ osal_mutex_unlock(mutex);
+ }
}
#else
@@ -59,27 +61,27 @@ TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
* \brief Write modes intended to allow special read and write functions to be able to
* copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
*/
-typedef enum
-{
- TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
+typedef enum {
+ TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
#ifdef TUP_MEM_CONST_ADDR
TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
#endif
} tu_fifo_copy_mode_t;
-bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
-{
+bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, uint16_t item_size, bool overwritable) {
// Limit index space to 2*depth - this allows for a fast "modulo" calculation
// but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
// only if overflow happens once (important for unsupervised DMA applications)
- if (depth > 0x8000) return false;
+ if (depth > 0x8000) {
+ return false;
+ }
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
- f->buffer = (uint8_t*) buffer;
+ f->buffer = (uint8_t *)buffer;
f->depth = depth;
- f->item_size = (uint16_t) (item_size & 0x7FFF);
+ f->item_size = (uint16_t)(item_size & 0x7FFF);
f->overwritable = overwritable;
f->rd_idx = 0;
f->wr_idx = 0;
@@ -98,22 +100,19 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
// Code adapted from dcd_synopsys.c
// TODO generalize with configurable 1 byte or 4 byte each read
-static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
-{
- volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf;
+static void _ff_push_const_addr(uint8_t *ff_buf, const void *app_buf, uint16_t len) {
+ const volatile uint32_t *reg_rx = (volatile const uint32_t *)app_buf;
// Reading full available 32 bit words from const app address
uint16_t full_words = len >> 2;
- while(full_words--)
- {
+ while (full_words--) {
tu_unaligned_write32(ff_buf, *reg_rx);
ff_buf += 4;
}
// Read the remaining 1-3 bytes from const app address
- uint8_t const bytes_rem = len & 0x03;
- if ( bytes_rem )
- {
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
uint32_t tmp32 = *reg_rx;
memcpy(ff_buf, &tmp32, bytes_rem);
}
@@ -121,22 +120,19 @@ static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t
// Intended to be used to write to hardware USB FIFO in e.g. STM32
// where all data is written to a constant address in full word copies
-static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
-{
- volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf;
+static void _ff_pull_const_addr(void *app_buf, const uint8_t *ff_buf, uint16_t len) {
+ volatile uint32_t *reg_tx = (volatile uint32_t *)app_buf;
// Write full available 32 bit words to const address
uint16_t full_words = len >> 2;
- while(full_words--)
- {
+ while (full_words--) {
*reg_tx = tu_unaligned_read32(ff_buf);
ff_buf += 4;
}
// Write the remaining 1-3 bytes into const address
- uint8_t const bytes_rem = len & 0x03;
- if ( bytes_rem )
- {
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
uint32_t tmp32 = 0;
memcpy(&tmp32, ff_buf, bytes_rem);
@@ -146,33 +142,27 @@ static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t
#endif
// send one item to fifo WITHOUT updating write pointer
-static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
-{
+static inline void _ff_push(tu_fifo_t *f, const void *app_buf, uint16_t rel) {
memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
}
// send n items to fifo WITHOUT updating write pointer
-static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode)
-{
- uint16_t const lin_count = f->depth - wr_ptr;
- uint16_t const wrap_count = n - lin_count;
+static void _ff_push_n(tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode) {
+ const uint16_t lin_count = f->depth - wr_ptr;
+ const uint16_t wrap_count = n - lin_count;
- uint16_t lin_bytes = lin_count * f->item_size;
+ uint16_t lin_bytes = lin_count * f->item_size;
uint16_t wrap_bytes = wrap_count * f->item_size;
// current buffer of fifo
- uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size);
+ uint8_t *ff_buf = f->buffer + (wr_ptr * f->item_size);
- switch (copy_mode)
- {
+ switch (copy_mode) {
case TU_FIFO_COPY_INC:
- if(n <= lin_count)
- {
+ if (n <= lin_count) {
// Linear only
- memcpy(ff_buf, app_buf, n*f->item_size);
- }
- else
- {
+ memcpy(ff_buf, app_buf, n * f->item_size);
+ } else {
// Wrap around
// Write data to linear part of buffer
@@ -180,19 +170,17 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
// Write data wrapped around
// TU_ASSERT(nWrap_bytes <= f->depth, );
- memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes);
+ memcpy(f->buffer, ((const uint8_t *)app_buf) + lin_bytes, wrap_bytes);
}
break;
+
#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
// Intended for hardware buffers from which it can be read word by word only
- if(n <= lin_count)
- {
+ if (n <= lin_count) {
// Linear only
- _ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
- }
- else
- {
+ _ff_push_const_addr(ff_buf, app_buf, n * f->item_size);
+ } else {
// Wrap around case
// Write full words to linear part of buffer
@@ -202,83 +190,80 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
// There could be odd 1-3 bytes before the wrap-around boundary
uint8_t rem = lin_bytes & 0x03;
- if (rem > 0)
- {
- volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
+ if (rem > 0) {
+ const volatile uint32_t *rx_fifo = (volatile const uint32_t *)app_buf;
- uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem);
+ uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem);
wrap_bytes -= remrem;
- uint32_t tmp32 = *rx_fifo;
- uint8_t * src_u8 = ((uint8_t *) &tmp32);
+ uint32_t tmp32 = *rx_fifo;
+ uint8_t *src_u8 = ((uint8_t *)&tmp32);
// Write 1-3 bytes before wrapped boundary
- while(rem--) *ff_buf++ = *src_u8++;
+ while (rem--) {
+ *ff_buf++ = *src_u8++;
+ }
// Read more bytes to beginning to complete a word
ff_buf = f->buffer;
- while(remrem--) *ff_buf++ = *src_u8++;
- }
- else
- {
+ while (remrem--) {
+ *ff_buf++ = *src_u8++;
+ }
+ } else {
ff_buf = f->buffer; // wrap around to beginning
}
// Write data wrapped part
- if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
+ if (wrap_bytes > 0) {
+ _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
+ }
}
break;
#endif
- default: break;
+
+ default:
+ break; // unknown mode
}
}
// get one item from fifo WITHOUT updating read pointer
-static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
-{
+static inline void _ff_pull(tu_fifo_t *f, void *app_buf, uint16_t rel) {
memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
}
// get n items from fifo WITHOUT updating read pointer
-static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode)
-{
- uint16_t const lin_count = f->depth - rd_ptr;
- uint16_t const wrap_count = n - lin_count; // only used if wrapped
+static void _ff_pull_n(tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode) {
+ const uint16_t lin_count = f->depth - rd_ptr;
+ const uint16_t wrap_count = n - lin_count; // only used if wrapped
- uint16_t lin_bytes = lin_count * f->item_size;
+ uint16_t lin_bytes = lin_count * f->item_size;
uint16_t wrap_bytes = wrap_count * f->item_size;
// current buffer of fifo
- uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size);
+ uint8_t *ff_buf = f->buffer + (rd_ptr * f->item_size);
- switch (copy_mode)
- {
+ switch (copy_mode) {
case TU_FIFO_COPY_INC:
- if ( n <= lin_count )
- {
+ if (n <= lin_count) {
// Linear only
- memcpy(app_buf, ff_buf, n*f->item_size);
- }
- else
- {
+ memcpy(app_buf, ff_buf, n * f->item_size);
+ } else {
// Wrap around
// Read data from linear part of buffer
memcpy(app_buf, ff_buf, lin_bytes);
// Read data wrapped part
- memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes);
+ memcpy((uint8_t *)app_buf + lin_bytes, f->buffer, wrap_bytes);
}
- break;
+ break;
+
#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
- if ( n <= lin_count )
- {
+ if (n <= lin_count) {
// Linear only
- _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
- }
- else
- {
+ _ff_pull_const_addr(app_buf, ff_buf, n * f->item_size);
+ } else {
// Wrap around case
// Read full words from linear part of buffer
@@ -288,36 +273,41 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
// There could be odd 1-3 bytes before the wrap-around boundary
uint8_t rem = lin_bytes & 0x03;
- if (rem > 0)
- {
- volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf;
+ if (rem > 0) {
+ volatile uint32_t *reg_tx = (volatile uint32_t *)app_buf;
- uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem);
+ uint8_t remrem = (uint8_t)tu_min16(wrap_bytes, 4 - rem);
wrap_bytes -= remrem;
- uint32_t tmp32=0;
- uint8_t * dst_u8 = (uint8_t *)&tmp32;
+ uint32_t tmp32 = 0;
+ uint8_t *dst_u8 = (uint8_t *)&tmp32;
// Read 1-3 bytes before wrapped boundary
- while(rem--) *dst_u8++ = *ff_buf++;
+ while (rem--) {
+ *dst_u8++ = *ff_buf++;
+ }
// Read more bytes from beginning to complete a word
ff_buf = f->buffer;
- while(remrem--) *dst_u8++ = *ff_buf++;
+ while (remrem--) {
+ *dst_u8++ = *ff_buf++;
+ }
*reg_tx = tmp32;
- }
- else
- {
+ } else {
ff_buf = f->buffer; // wrap around to beginning
}
// Read data wrapped part
- if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
+ if (wrap_bytes > 0) {
+ _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
+ }
}
- break;
+ break;
#endif
- default: break;
+
+ default:
+ break; // unknown mode
}
}
@@ -326,24 +316,18 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
//--------------------------------------------------------------------+
// return only the index difference and as such can be used to determine an overflow i.e overflowable count
-TU_ATTR_ALWAYS_INLINE static inline
-uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
// In case we have non-power of two depth we need a further modification
- if (wr_idx >= rd_idx)
- {
- return (uint16_t) (wr_idx - rd_idx);
- } else
- {
- return (uint16_t) (2*depth - (rd_idx - wr_idx));
+ if (wr_idx >= rd_idx) {
+ return (uint16_t)(wr_idx - rd_idx);
+ } else {
+ return (uint16_t)(2 * depth - (rd_idx - wr_idx));
}
}
// return remaining slot in fifo
-TU_ATTR_ALWAYS_INLINE static inline
-uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx)
-{
- uint16_t const count = _ff_count(depth, wr_idx, rd_idx);
+TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx) {
+ const uint16_t count = _ff_count(depth, wr_idx, rd_idx);
return (depth > count) ? (depth - count) : 0;
}
@@ -353,16 +337,14 @@ uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx)
// Advance an absolute index
// "absolute" index is only in the range of [0..2*depth)
-static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset)
-{
+static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) {
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
- uint16_t new_idx = (uint16_t) (idx + offset);
- if ( (idx > new_idx) || (new_idx >= 2*depth) )
- {
- uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1));
- new_idx = (uint16_t) (new_idx + non_used_index_space);
+ uint16_t new_idx = (uint16_t)(idx + offset);
+ if ((idx > new_idx) || (new_idx >= 2 * depth)) {
+ const uint16_t non_used_index_space = (uint16_t)(UINT16_MAX - (2 * depth - 1));
+ new_idx = (uint16_t)(new_idx + non_used_index_space);
}
return new_idx;
@@ -370,14 +352,12 @@ static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset)
#if 0 // not used but
// Backward an absolute index
-static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset)
-{
+static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset) {
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
uint16_t new_idx = (uint16_t) (idx - offset);
- if ( (idx < new_idx) || (new_idx >= 2*depth) )
- {
+ if ( (idx < new_idx) || (new_idx >= 2*depth) ) {
uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1));
new_idx = (uint16_t) (new_idx - non_used_index_space);
}
@@ -387,26 +367,22 @@ static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset)
#endif
// index to pointer, simply an modulo with minus.
-TU_ATTR_ALWAYS_INLINE static inline
-uint16_t idx2ptr(uint16_t depth, uint16_t idx)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t idx) {
// Only run at most 3 times since index is limit in the range of [0..2*depth)
- while ( idx >= depth ) idx -= depth;
+ while (idx >= depth) {
+ idx -= depth;
+ }
return idx;
}
// Works on local copies of w
// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms
// an full fifo i.e _ff_count() = depth
-TU_ATTR_ALWAYS_INLINE static inline
-uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t _ff_correct_read_index(tu_fifo_t *f, uint16_t wr_idx) {
uint16_t rd_idx;
- if ( wr_idx >= f->depth )
- {
+ if (wr_idx >= f->depth) {
rd_idx = wr_idx - f->depth;
- }else
- {
+ } else {
rd_idx = wr_idx + f->depth;
}
@@ -417,18 +393,17 @@ uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx)
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
-static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx)
-{
+static bool _tu_fifo_peek(tu_fifo_t *f, void *p_buffer, uint16_t wr_idx, uint16_t rd_idx) {
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
// nothing to peek
- if ( cnt == 0 ) return false;
+ if (cnt == 0) {
+ return false;
+ }
// Check overflow and correct if required
- if ( cnt > f->depth )
- {
+ if (cnt > f->depth) {
rd_idx = _ff_correct_read_index(f, wr_idx);
- cnt = f->depth;
}
uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
@@ -441,22 +416,25 @@ static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
-static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode)
-{
+static uint16_t _tu_fifo_peek_n(
+ tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode) {
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
// nothing to peek
- if ( cnt == 0 ) return 0;
+ if (cnt == 0) {
+ return 0;
+ }
// Check overflow and correct if required
- if ( cnt > f->depth )
- {
+ if (cnt > f->depth) {
rd_idx = _ff_correct_read_index(f, wr_idx);
- cnt = f->depth;
+ cnt = f->depth;
}
// Check if we can read something at and after offset - if too less is available we read what remains
- if ( cnt < n ) n = cnt;
+ if (cnt < n) {
+ n = cnt;
+ }
uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
@@ -466,40 +444,36 @@ static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint1
return n;
}
-static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
-{
- if ( n == 0 ) return 0;
+static uint16_t _tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n, tu_fifo_copy_mode_t copy_mode) {
+ if (n == 0) {
+ return 0;
+ }
_ff_lock(f->mutex_wr);
uint16_t wr_idx = f->wr_idx;
uint16_t rd_idx = f->rd_idx;
- uint8_t const* buf8 = (uint8_t const*) data;
+ const uint8_t *buf8 = (const uint8_t *)data;
- TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ",
- rd_idx, wr_idx, _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n);
+ TU_LOG(
+ TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx,
+ _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n);
- if ( !f->overwritable )
- {
+ if (!f->overwritable) {
// limit up to full
- uint16_t const remain = _ff_remaining(f->depth, wr_idx, rd_idx);
- n = tu_min16(n, remain);
- }
- else
- {
+ const uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx);
+ n = tu_min16(n, remain);
+ } else {
// In over-writable mode, fifo_write() is allowed even when fifo is full. In such case,
// oldest data in fifo i.e at read pointer data will be overwritten
// Note: we can modify read buffer contents but we must not modify the read index itself within a write function!
// Since it would end up in a race condition with read functions!
- if ( n >= f->depth )
- {
+ if (n >= f->depth) {
// Only copy last part
- if ( copy_mode == TU_FIFO_COPY_INC )
- {
+ if (copy_mode == TU_FIFO_COPY_INC) {
buf8 += (n - f->depth) * f->item_size;
- }else
- {
+ } else {
// TODO should read from hw fifo to discard data, however reading an odd number could
// accidentally discard data.
}
@@ -508,12 +482,9 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu
// We start writing at the read pointer's position since we fill the whole buffer
wr_idx = rd_idx;
- }
- else
- {
- uint16_t const overflowable_count = _ff_count(f->depth, wr_idx, rd_idx);
- if (overflowable_count + n >= 2*f->depth)
- {
+ } else {
+ const uint16_t overflowable_count = _ff_count(f->depth, wr_idx, rd_idx);
+ if (overflowable_count + n >= 2 * f->depth) {
// Double overflowed
// Index is bigger than the allowed range [0,2*depth)
// re-position write index to have a full fifo after pushed
@@ -523,8 +494,7 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu
// However memmove() is expensive due to actual copying + wrapping consideration.
// Also race condition could happen anyway if read() is invoke while moving result in corrupted memory
// currently deliberately not implemented --> result in incorrect data read back
- }else
- {
+ } else {
// normal + single overflowed:
// Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read()
// Therefore we just increase write index
@@ -533,16 +503,11 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu
}
}
- if (n)
- {
+ if (n) {
uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
-
TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr);
- // Write data
_ff_push_n(f, buf8, n, wr_ptr, copy_mode);
-
- // Advance index
f->wr_idx = advance_index(f->depth, wr_idx, n);
TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx);
@@ -553,8 +518,7 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu
return n;
}
-static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
-{
+static uint16_t _tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode) {
_ff_lock(f->mutex_rd);
// Peek the data
@@ -587,31 +551,12 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo
@returns Number of items in FIFO
*/
/******************************************************************************/
-uint16_t tu_fifo_count(tu_fifo_t* f)
-{
+uint16_t tu_fifo_count(const tu_fifo_t *f) {
return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth);
}
/******************************************************************************/
/*!
- @brief Check if FIFO is empty.
-
- As this function only reads the read and write pointers once, this function is
- reentrant and thus thread and ISR save without any mutexes.
-
- @param[in] f
- Pointer to the FIFO buffer to manipulate
-
- @returns Number of items in FIFO
- */
-/******************************************************************************/
-bool tu_fifo_empty(tu_fifo_t* f)
-{
- return f->wr_idx == f->rd_idx;
-}
-
-/******************************************************************************/
-/*!
@brief Check if FIFO is full.
As this function only reads the read and write pointers once, this function is
@@ -623,8 +568,7 @@ bool tu_fifo_empty(tu_fifo_t* f)
@returns Number of items in FIFO
*/
/******************************************************************************/
-bool tu_fifo_full(tu_fifo_t* f)
-{
+bool tu_fifo_full(const tu_fifo_t *f) {
return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth;
}
@@ -641,8 +585,7 @@ bool tu_fifo_full(tu_fifo_t* f)
@returns Number of items in FIFO
*/
/******************************************************************************/
-uint16_t tu_fifo_remaining(tu_fifo_t* f)
-{
+uint16_t tu_fifo_remaining(const tu_fifo_t *f) {
return _ff_remaining(f->depth, f->wr_idx, f->rd_idx);
}
@@ -667,14 +610,12 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f)
@returns True if overflow happened
*/
/******************************************************************************/
-bool tu_fifo_overflowed(tu_fifo_t* f)
-{
+bool tu_fifo_overflowed(const tu_fifo_t *f) {
return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth;
}
// Only use in case tu_fifo_overflow() returned true!
-void tu_fifo_correct_read_pointer(tu_fifo_t* f)
-{
+void tu_fifo_correct_read_pointer(tu_fifo_t *f) {
_ff_lock(f->mutex_rd);
_ff_correct_read_index(f, f->wr_idx);
_ff_unlock(f->mutex_rd);
@@ -696,8 +637,7 @@ void tu_fifo_correct_read_pointer(tu_fifo_t* f)
@returns TRUE if the queue is not empty
*/
/******************************************************************************/
-bool tu_fifo_read(tu_fifo_t* f, void * buffer)
-{
+bool tu_fifo_read(tu_fifo_t *f, void *buffer) {
_ff_lock(f->mutex_rd);
// Peek the data
@@ -727,8 +667,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
@returns number of items read from the FIFO
*/
/******************************************************************************/
-uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
-{
+uint16_t tu_fifo_read_n(tu_fifo_t *f, void *buffer, uint16_t n) {
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
}
@@ -750,8 +689,7 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
@returns number of items read from the FIFO
*/
/******************************************************************************/
-uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
-{
+uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t *f, void *buffer, uint16_t n) {
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
#endif
@@ -769,8 +707,7 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint1
@returns TRUE if the queue is not empty
*/
/******************************************************************************/
-bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
-{
+bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) {
_ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx);
_ff_unlock(f->mutex_rd);
@@ -792,8 +729,7 @@ bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
@returns Number of bytes written to p_buffer
*/
/******************************************************************************/
-uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n)
-{
+uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) {
_ff_lock(f->mutex_rd);
uint16_t ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
_ff_unlock(f->mutex_rd);
@@ -816,27 +752,19 @@ uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n)
FIFO will always return TRUE)
*/
/******************************************************************************/
-bool tu_fifo_write(tu_fifo_t* f, const void * data)
-{
+bool tu_fifo_write(tu_fifo_t *f, const void *data) {
_ff_lock(f->mutex_wr);
- bool ret;
- uint16_t const wr_idx = f->wr_idx;
+ bool ret;
+ const uint16_t wr_idx = f->wr_idx;
- if ( tu_fifo_full(f) && !f->overwritable )
- {
+ if (tu_fifo_full(f) && !f->overwritable) {
ret = false;
- }else
- {
+ } else {
uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
-
- // Write data
_ff_push(f, data, wr_ptr);
-
- // Advance pointer
f->wr_idx = advance_index(f->depth, wr_idx, 1);
-
- ret = true;
+ ret = true;
}
_ff_unlock(f->mutex_wr);
@@ -858,8 +786,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
@return Number of written elements
*/
/******************************************************************************/
-uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
-{
+uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n) {
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
}
@@ -879,8 +806,7 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
@return Number of written elements
*/
/******************************************************************************/
-uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
-{
+uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t *f, const void *data, uint16_t n) {
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
#endif
@@ -893,8 +819,7 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data,
Pointer to the FIFO buffer to manipulate
*/
/******************************************************************************/
-bool tu_fifo_clear(tu_fifo_t *f)
-{
+bool tu_fifo_clear(tu_fifo_t *f) {
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
@@ -948,8 +873,7 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
Number of items the write pointer moves forward
*/
/******************************************************************************/
-void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
-{
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) {
f->wr_idx = advance_index(f->depth, f->wr_idx, n);
}
@@ -969,8 +893,7 @@ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
Number of items the read pointer moves forward
*/
/******************************************************************************/
-void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
-{
+void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) {
f->rd_idx = advance_index(f->depth, f->rd_idx, n);
}
@@ -989,8 +912,7 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
Pointer to struct which holds the desired infos
*/
/******************************************************************************/
-void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
-{
+void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) {
// Operate on temporary values in case they change in between
uint16_t wr_idx = f->wr_idx;
uint16_t rd_idx = f->rd_idx;
@@ -998,8 +920,7 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
// Check overflow and correct if required - may happen in case a DMA wrote too fast
- if (cnt > f->depth)
- {
+ if (cnt > f->depth) {
_ff_lock(f->mutex_rd);
rd_idx = _ff_correct_read_index(f, wr_idx);
_ff_unlock(f->mutex_rd);
@@ -1008,8 +929,7 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
}
// Check if fifo is empty
- if (cnt == 0)
- {
+ if (cnt == 0) {
info->len_lin = 0;
info->len_wrap = 0;
info->ptr_lin = NULL;
@@ -1025,17 +945,14 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
info->ptr_lin = &f->buffer[rd_ptr];
// Check if there is a wrap around necessary
- if (wr_ptr > rd_ptr)
- {
+ if (wr_ptr > rd_ptr) {
// Non wrapping case
- info->len_lin = cnt;
+ info->len_lin = cnt;
info->len_wrap = 0;
info->ptr_wrap = NULL;
- }
- else
- {
- info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full
+ } else {
+ info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full
info->len_wrap = cnt - info->len_lin;
info->ptr_wrap = f->buffer;
@@ -1057,14 +974,12 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
Pointer to struct which holds the desired infos
*/
/******************************************************************************/
-void tu_fifo_get_write_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) {
uint16_t wr_idx = f->wr_idx;
uint16_t rd_idx = f->rd_idx;
uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx);
- if (remain == 0)
- {
+ if (remain == 0) {
info->len_lin = 0;
info->len_wrap = 0;
info->ptr_lin = NULL;
@@ -1079,15 +994,12 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
// Copy pointer to buffer to start writing to
info->ptr_lin = &f->buffer[wr_ptr];
- if (wr_ptr < rd_ptr)
- {
+ if (wr_ptr < rd_ptr) {
// Non wrapping case
- info->len_lin = rd_ptr-wr_ptr;
+ info->len_lin = rd_ptr - wr_ptr;
info->len_wrap = 0;
info->ptr_wrap = NULL;
- }
- else
- {
+ } else {
info->len_lin = f->depth - wr_ptr;
info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth
info->ptr_wrap = f->buffer; // Always start of buffer
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 879acda4f..9d8b864e9 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_FIFO_H_
-#define _TUSB_FIFO_H_
+#ifndef TUSB_FIFO_H_
+#define TUSB_FIFO_H_
#ifdef __cplusplus
extern "C" {
@@ -104,16 +104,16 @@ extern "C" {
* | R | 1 | 2 | W | 4 | 5 |
*/
typedef struct {
- uint8_t* buffer ; // buffer pointer
- uint16_t depth ; // max items
+ 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
+ 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
+ volatile uint16_t wr_idx; // write index
+ volatile uint16_t rd_idx; // read index
#if OSAL_MUTEX_REQUIRED
osal_mutex_t mutex_wr;
@@ -129,12 +129,13 @@ typedef struct {
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_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)]; \
@@ -154,33 +155,35 @@ void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_m
#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_write(tu_fifo_t *f, void const *data);
+uint16_t tu_fifo_write_n(tu_fifo_t *f, const void *data, uint16_t n);
+
+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);
-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);
+uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t *f, const void *data, uint16_t n);
+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);
+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);
+uint16_t tu_fifo_count(const tu_fifo_t *f);
+uint16_t tu_fifo_remaining(const tu_fifo_t *f);
+bool tu_fifo_full(const tu_fifo_t *f);
+bool tu_fifo_overflowed(const tu_fifo_t *f);
-TU_ATTR_ALWAYS_INLINE static inline
-uint16_t tu_fifo_depth(tu_fifo_t* f) {
+TU_ATTR_ALWAYS_INLINE static inline bool tu_fifo_empty(const tu_fifo_t *f) {
+ return f->wr_idx == f->rd_idx;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_fifo_depth(const tu_fifo_t *f) {
return f->depth;
}
+void tu_fifo_correct_read_pointer(tu_fifo_t *f);
+
// 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);
@@ -196,4 +199,4 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
}
#endif
-#endif /* _TUSB_FIFO_H_ */
+#endif
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index a0175d664..002cd3a0e 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -37,14 +37,14 @@
#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
+ #define TUP_ARCH_STRICT_ALIGN 0
#else
- #define TUP_ARCH_STRICT_ALIGN 1
+ #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
+ #define TUP_ARCH_STRICT_ALIGN 1
#endif
/* USB Controller Attributes for Device, Host or MCU (both)
@@ -57,36 +57,41 @@
//--------------------------------------------------------------------+
// NXP
//--------------------------------------------------------------------+
-#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
+#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
#define TUP_USBIP_IP3511
- #define TUP_DCD_ENDPOINT_MAX 5
+ #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_DCD_ENDPOINT_MAX 16
#define TUP_USBIP_OHCI
- #define TUP_OHCI_RHPORTS 2
+ #define TUP_USBIP_OHCI_NXP
+ #define TUP_OHCI_RHPORTS 2
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
- #define TUP_USBIP_IP3511
- #define TUP_DCD_ENDPOINT_MAX 5
+ #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
+ #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
+ #define TUP_USBIP_OHCI
+ #define TUP_USBIP_OHCI_NXP
+ #define TUP_OHCI_RHPORTS 1 // 1 downstream port
+
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 6
+ #define TUP_RHPORT_HIGHSPEED 1
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
// USB0 is chipidea FS
@@ -97,15 +102,15 @@
#define TUP_USBIP_CHIPIDEA_HS
#define TUP_USBIP_EHCI
- #define TUP_DCD_ENDPOINT_MAX 8
- #define TUP_RHPORT_HIGHSPEED 1
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 16
#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX)
#include "fsl_device_registers.h"
@@ -113,54 +118,61 @@
#define TUP_USBIP_CHIPIDEA_HS
#define TUP_USBIP_EHCI
- #define TUP_DCD_ENDPOINT_MAX 8
- #define TUP_RHPORT_HIGHSPEED 1
+ #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 32
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 16
#elif TU_CHECK_MCU(OPT_MCU_MM32F327X)
- #define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_DCD_EDPT_CLOSE_API
//--------------------------------------------------------------------+
// Nordic
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
// 8 CBI + 1 ISO
- #define TUP_DCD_ENDPOINT_MAX 9
+ #define TUP_DCD_ENDPOINT_MAX 9
+ #define TUP_DCD_EDPT_CLOSE_API
+
+#elif TU_CHECK_MCU(OPT_MCU_NRF54)
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_NRF
+ #define TUP_DCD_ENDPOINT_MAX 16
+ #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
//--------------------------------------------------------------------+
// 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_SAMD11, OPT_MCU_SAML2X, OPT_MCU_SAMD21) || TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X)
+ #define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_SAMG)
- #define TUP_DCD_ENDPOINT_MAX 6
+ #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 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 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
+#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
+ #define TUP_DCD_EDPT_CLOSE_API
//--------------------------------------------------------------------+
// ST
@@ -168,23 +180,23 @@
#elif TU_CHECK_MCU(OPT_MCU_STM32F0)
#define TUP_USBIP_FSDEV
#define TUP_USBIP_FSDEV_STM32
- #define TUP_DCD_ENDPOINT_MAX 8
+ #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)
+ #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_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
+ #define TUP_DCD_ENDPOINT_MAX 8
#else
#error "Unsupported STM32F1 mcu"
#endif
@@ -194,42 +206,55 @@
#define TUP_USBIP_DWC2_STM32
// FS has 4 ep, HS has 5 ep
- #define TUP_DCD_ENDPOINT_MAX 6
+ #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
+ #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
+ #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
+ #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
+ #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
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32G4)
// Device controller
@@ -238,41 +263,40 @@
// TypeC controller
#define TUP_USBIP_TYPEC_STM32
- #define TUP_DCD_ENDPOINT_MAX 8
+ #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
+ #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
+ #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
+ #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)
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 6
#elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \
- defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx)
+ defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx)
#define TUP_USBIP_FSDEV
#define TUP_USBIP_FSDEV_STM32
- #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_DCD_ENDPOINT_MAX 8
#else
#error "Unsupported STM32L4 mcu"
#endif
@@ -280,13 +304,19 @@
#elif TU_CHECK_MCU(OPT_MCU_STM32WB)
#define TUP_USBIP_FSDEV
#define TUP_USBIP_FSDEV_STM32
- #define TUP_DCD_ENDPOINT_MAX 8
+ #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)
+ #if defined(STM32U535xx) || defined(STM32U545xx)
#define TUP_USBIP_FSDEV
#define TUP_USBIP_FSDEV_STM32
- #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_DCD_ENDPOINT_MAX 8
#else
#define TUP_USBIP_DWC2
@@ -294,179 +324,206 @@
// 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
+ 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
+ #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
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 8
-#elif TU_CHECK_MCU(OPT_MCU_STM32H7RS)
+#elif TU_CHECK_MCU(OPT_MCU_STM32U3)
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 9
// MCU with on-chip HS Phy
- #define TUP_RHPORT_HIGHSPEED 1
+ #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 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
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 8
//--------------------------------------------------------------------+
// ValentyUSB (Litex)
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI)
- #define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_DCD_ENDPOINT_MAX 16
//--------------------------------------------------------------------+
// Nuvoton
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126)
- #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_NUC120)
- #define TUP_DCD_ENDPOINT_MAX 6
+ #define TUP_DCD_ENDPOINT_MAX 6
#elif TU_CHECK_MCU(OPT_MCU_NUC505)
- #define TUP_DCD_ENDPOINT_MAX 12
- #define TUP_RHPORT_HIGHSPEED 1
+ #define TUP_DCD_ENDPOINT_MAX 12
+ #define TUP_RHPORT_HIGHSPEED 1
//--------------------------------------------------------------------+
// Espressif
//--------------------------------------------------------------------+
-#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#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/
+ #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
+
+ // clang-format off
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+ // clang-format on
+
+ #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
+ #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 TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS
+ #define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep
+
+ // clang-format off
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+ // clang-format on
- #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
+ #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
+ #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_ESP32C6, OPT_MCU_ESP32H2)
+#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"
+ #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/
+ #define TUP_DCD_ENDPOINT_MAX 0
+
+ // clang-format off
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+ // clang-format on
+
//--------------------------------------------------------------------+
// Dialog
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_DA1469X)
- #define TUP_DCD_ENDPOINT_MAX 4
+ #define TUP_DCD_ENDPOINT_MAX 4
+ #define TUP_DCD_EDPT_CLOSE_API
//--------------------------------------------------------------------+
// Raspberry Pi
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
- #define TUP_DCD_EDPT_ISO_ALLOC
- #define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_DCD_ENDPOINT_MAX 16
+ #define TUP_MCU_MULTIPLE_CORE 1
- #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))
+ #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
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 10
//--------------------------------------------------------------------+
// GigaDevice
//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
#define TUP_USBIP_DWC2
- #define TUP_DCD_ENDPOINT_MAX 4
+ #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
+ #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
+ #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 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 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
+ #define TUP_DCD_ENDPOINT_MAX 4
//--------------------------------------------------------------------+
// WCH
@@ -476,40 +533,50 @@
#define TUP_USBIP_WCH_USBFS
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
- #define CFG_TUD_WCH_USBIP_USBFS 0
+ #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)
+ #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)
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+ #if CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_EDPT_CLOSE_API
+ #endif
#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
+ #define CFG_TUD_WCH_USBIP_USBFS 1
#endif
- #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
- // v20x support both FSDEV (USBD) and USBFS, default to FSDEV
+ // 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
+ #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)
+ #define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
#endif
- #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
// v307 support both FS and HS, default to HS
@@ -517,15 +584,19 @@
#define TUP_USBIP_WCH_USBFS
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
- #define CFG_TUD_WCH_USBIP_USBFS 0
+ #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)
+ #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)
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+ #if CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_EDPT_CLOSE_API
+ #endif
//--------------------------------------------------------------------+
// Analog Devices
@@ -533,10 +604,55 @@
#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 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
//--------------------------------------------------------------------+
@@ -545,7 +661,7 @@
#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))
+ #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4 * (CFG_TUH_DEVICE_MAX - 1))
#endif
#endif
@@ -555,17 +671,17 @@
//--------------------------------------------------------------------+
#ifndef TUP_MCU_MULTIPLE_CORE
-#define TUP_MCU_MULTIPLE_CORE 0
+ #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
+ #define TUP_DCD_ENDPOINT_MAX 8
#endif
// Default to fullspeed if not defined
#ifndef TUP_RHPORT_HIGHSPEED
- #define TUP_RHPORT_HIGHSPEED 0
+ #define TUP_RHPORT_HIGHSPEED 0
#endif
// fast function, normally mean placing function in SRAM
@@ -573,8 +689,12 @@
#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)
+#if defined(TUP_USBIP_IP3511) || defined(TUP_USBIP_RUSB2)
+ #define TUP_DCD_EDPT_CLOSE_API
+#endif
+
+// USBIP implement dcd_edpt_close() and does not support ISO alloc & activate API
+#ifndef TUP_DCD_EDPT_CLOSE_API
#define TUP_DCD_EDPT_ISO_ALLOC
#endif
diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h
index 31aca8a31..be1264a71 100644
--- a/src/common/tusb_private.h
+++ b/src/common/tusb_private.h
@@ -40,6 +40,12 @@ extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM];
// Endpoint
//--------------------------------------------------------------------+
+enum {
+ TU_EDPT_STATE_BUSY = 0x01,
+ TU_EDPT_STATE_STALLED = 0x02,
+ TU_EDPT_STATE_CLAIMED = 0x04,
+};
+
typedef struct TU_ATTR_PACKED {
volatile uint8_t busy : 1;
volatile uint8_t stalled : 1;
@@ -48,8 +54,8 @@ typedef struct TU_ATTR_PACKED {
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
+ bool is_host : 1; // 1: host, 0: device
+ bool is_mps512 : 1; // 1: 512, 0: 64 since stream is used for Bulk only
};
uint8_t ep_addr;
uint16_t ep_bufsize;
@@ -93,24 +99,27 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove
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);
+TU_ATTR_ALWAYS_INLINE static inline void tu_edpt_stream_open(tu_edpt_stream_t* s, tusb_desc_endpoint_t const *desc_ep) {
s->ep_addr = desc_ep->bEndpointAddress;
- s->is_mps512 = (tu_edpt_packet_size(desc_ep) == 512) ? 1 : 0;
+ s->is_mps512 = tu_edpt_packet_size(desc_ep) == 512;
}
-TU_ATTR_ALWAYS_INLINE static inline
-void tu_edpt_stream_close(tu_edpt_stream_t* s) {
+TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_is_opened(const tu_edpt_stream_t *s) {
+ return s->ep_addr != 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) {
+TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
return tu_fifo_clear(&s->ff);
}
+TU_ATTR_ALWAYS_INLINE static inline bool tu_edpt_stream_empty(tu_edpt_stream_t *s) {
+ return tu_fifo_empty(&s->ff);
+}
+
//--------------------------------------------------------------------+
// Stream Write
//--------------------------------------------------------------------+
@@ -118,7 +127,7 @@ bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
// 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
+// Start an usb transfer if endpoint is not busy. Return number of queued bytes
uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s);
// Start an zero-length packet if needed
@@ -141,27 +150,25 @@ 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)) {
+ if (0u != 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)) {
+void tu_edpt_stream_read_xfer_complete_with_buf(tu_edpt_stream_t *s, const void *buf, uint32_t xferred_bytes) {
+ if (0u != 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) {
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_edpt_stream_read_available(const 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) {
+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);
}
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index e000a4bd3..73c816e3e 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -43,14 +43,14 @@
#define TUD_EPBUF_DEF(_name, _size) \
union { \
CFG_TUD_MEM_ALIGN uint8_t _name[_size]; \
- uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(_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; \
- uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ 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 -------------//
@@ -61,14 +61,14 @@
#define TUH_EPBUF_DEF(_name, _size) \
union { \
CFG_TUH_MEM_ALIGN uint8_t _name[_size]; \
- uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(_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; \
- uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ 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))]; \
}
@@ -77,7 +77,7 @@
*------------------------------------------------------------------*/
typedef enum {
- TUSB_ROLE_INVALID = 0,
+ TUSB_ROLE_INVALID = 0u,
TUSB_ROLE_DEVICE = 0x1,
TUSB_ROLE_HOST = 0x2,
} tusb_role_t;
@@ -103,6 +103,7 @@ typedef enum {
TUSB_DIR_OUT = 0,
TUSB_DIR_IN = 1,
+ TUSB_EPNUM_MASK = 0x0F,
TUSB_DIR_IN_MASK = 0x80
} tusb_dir_t;
@@ -178,7 +179,7 @@ typedef enum {
} tusb_request_feature_selector_t;
typedef enum {
- TUSB_REQ_TYPE_STANDARD = 0,
+ TUSB_REQ_TYPE_STANDARD = 0u,
TUSB_REQ_TYPE_CLASS,
TUSB_REQ_TYPE_VENDOR,
TUSB_REQ_TYPE_INVALID
@@ -252,8 +253,8 @@ typedef enum {
} device_capability_type_t;
enum {
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
- TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
+ TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1 << 5,
+ TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1 << 6,
};
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
@@ -278,6 +279,8 @@ typedef enum {
XFER_RESULT_INVALID
} xfer_result_t;
+#define tusb_xfer_result_t xfer_result_t
+
// TODO remove
enum {
DESC_OFFSET_LEN = 0,
@@ -309,7 +312,7 @@ enum {
};
enum {
- TUSB_INDEX_INVALID_8 = 0xFFu
+ TUSB_INDEX_INVALID_8 = 0xFF
};
//--------------------------------------------------------------------+
@@ -345,11 +348,10 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18u, "size is not correct");
// USB Binary Device Object Store (BOS) Descriptor
typedef struct TU_ATTR_PACKED {
@@ -359,7 +361,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5u, "size is not correct");
/// USB Configuration Descriptor
typedef struct TU_ATTR_PACKED {
@@ -374,7 +376,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9u, "size is not correct");
/// USB Interface Descriptor
typedef struct TU_ATTR_PACKED {
@@ -390,7 +392,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9u, "size is not correct");
/// USB Endpoint Descriptor
typedef struct TU_ATTR_PACKED {
@@ -410,7 +412,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7u, "size is not correct");
/// USB Other Speed Configuration Descriptor
typedef struct TU_ATTR_PACKED {
@@ -440,7 +442,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10u, "size is not correct");
/// USB Interface Association Descriptor (IAD ECN)
typedef struct TU_ATTR_PACKED {
@@ -457,7 +459,7 @@ typedef struct TU_ATTR_PACKED {
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");
+TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8u, "size is not correct");
// USB String Descriptor
typedef struct TU_ATTR_PACKED {
@@ -527,7 +529,7 @@ typedef struct TU_ATTR_PACKED {
uint16_t wLength;
} tusb_control_request_t;
-TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
+TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8u, "size is not correct");
TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END
@@ -543,11 +545,11 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
// 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));
+ return (uint8_t) (addr & TUSB_EPNUM_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));
+ return (uint8_t) (num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0u));
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
@@ -561,40 +563,6 @@ TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_
}
#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];
-}
-
-// 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
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 6d02d3572..587554e7f 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -53,11 +53,11 @@
* 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_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;}
+ * #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
@@ -67,10 +67,8 @@
//--------------------------------------------------------------------+
// TU_VERIFY Helper
//--------------------------------------------------------------------+
-
#if CFG_TUSB_DEBUG
- #include <stdio.h>
- #define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
+ #define TU_MESS_FAILED() TU_LOG1("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
#else
#define TU_MESS_FAILED() do {} while (0)
#endif
@@ -80,7 +78,7 @@
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 */ \
+ if (0u != ((*ARM_CM_DHCSR) & 1UL)) { __asm("BKPT #0\n"); } /* Only halt mcu if debugger is attached */ \
} while(0)
#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF
@@ -100,7 +98,7 @@
*------------------------------------------------------------------*/
#define TU_VERIFY_DEFINE(_cond, _ret) \
do { \
- if ( !(_cond) ) { return _ret; } \
+ if (!(_cond)) { return _ret; } \
} while(0)
#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false)
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 789552d47..353f836ee 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -79,7 +79,7 @@ typedef struct TU_ATTR_ALIGNED(4) {
// FUNC_CALL
struct {
- void (*func) (void*);
+ void (*func) (void* param);
void* param;
}func_call;
};
@@ -162,7 +162,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer
// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
// This API is optional, may be useful for register-based for transferring data.
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK;
+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);
@@ -171,7 +171,12 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
// 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
+#ifdef TUP_DCD_EDPT_CLOSE_API
+// Close an endpoint.
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr);
+
+#else
+
// 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);
@@ -179,10 +184,6 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
// 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
//--------------------------------------------------------------------+
@@ -214,7 +215,7 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport
dcd_event_t event;
event.rhport = rhport;
event.event_id = DCD_EVENT_SETUP_RECEIVED;
- memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
+ (void) memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
dcd_event_handler(&event, in_isr);
}
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 9c381d5e0..cf96f050d 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -163,6 +163,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = cdcd_open,
.control_xfer_cb = cdcd_control_xfer_cb,
.xfer_cb = cdcd_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -176,6 +177,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = mscd_open,
.control_xfer_cb = mscd_control_xfer_cb,
.xfer_cb = mscd_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -189,6 +191,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = hidd_open,
.control_xfer_cb = hidd_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -202,6 +205,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = audiod_open,
.control_xfer_cb = audiod_control_xfer_cb,
.xfer_cb = audiod_xfer_cb,
+ .xfer_isr = audiod_xfer_isr,
.sof = audiod_sof_isr
},
#endif
@@ -215,6 +219,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = videod_open,
.control_xfer_cb = videod_control_xfer_cb,
.xfer_cb = videod_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -228,6 +233,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.reset = midid_reset,
.control_xfer_cb = midid_control_xfer_cb,
.xfer_cb = midid_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -241,6 +247,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = vendord_open,
.control_xfer_cb = tud_vendor_control_xfer_cb,
.xfer_cb = vendord_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -254,6 +261,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = usbtmcd_open_cb,
.control_xfer_cb = usbtmcd_control_xfer_cb,
.xfer_cb = usbtmcd_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -267,6 +275,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = dfu_rtd_open,
.control_xfer_cb = dfu_rtd_control_xfer_cb,
.xfer_cb = NULL,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -280,6 +289,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = dfu_moded_open,
.control_xfer_cb = dfu_moded_control_xfer_cb,
.xfer_cb = NULL,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -293,7 +303,8 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = netd_open,
.control_xfer_cb = netd_control_xfer_cb,
.xfer_cb = netd_xfer_cb,
- .sof = NULL,
+ .xfer_isr = NULL,
+ .sof = NULL,
},
#endif
@@ -306,6 +317,21 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = btd_open,
.control_xfer_cb = btd_control_xfer_cb,
.xfer_cb = btd_xfer_cb,
+ .xfer_isr = NULL,
+ .sof = NULL
+ },
+ #endif
+
+ #if CFG_TUD_MTP
+ {
+ .name = DRIVER_NAME("MTP"),
+ .init = mtpd_init,
+ .deinit = mtpd_deinit,
+ .reset = mtpd_reset,
+ .open = mtpd_open,
+ .control_xfer_cb = mtpd_control_xfer_cb,
+ .xfer_cb = mtpd_xfer_cb,
+ .xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -317,38 +343,41 @@ enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
tu_static usbd_class_driver_t const * _app_driver = NULL;
tu_static uint8_t _app_driver_count = 0;
-#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
+#define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + BUILTIN_DRIVER_COUNT))
// virtually joins built-in and application drivers together.
// Application is positioned first to allow overwriting built-in ones.
TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) {
- usbd_class_driver_t const * driver = NULL;
- if ( drvid < _app_driver_count ) {
+ usbd_class_driver_t const *driver = NULL;
+ if (drvid < _app_driver_count) {
// Application drivers
driver = &_app_driver[drvid];
- } else if ( drvid < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0 ){
- driver = &_usbd_driver[drvid - _app_driver_count];
+ } else{
+ drvid -= _app_driver_count;
+ if (drvid < BUILTIN_DRIVER_COUNT) {
+ driver = &_usbd_driver[drvid];
+ }
}
+
return driver;
}
-
//--------------------------------------------------------------------+
// DCD Event
//--------------------------------------------------------------------+
-
enum { RHPORT_INVALID = 0xFFu };
tu_static uint8_t _usbd_rhport = RHPORT_INVALID;
-// Event queue
-// usbd_int_set() is used as mutex in OS NONE config
+static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set);
+
+// Event queue: usbd_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
-tu_static osal_queue_t _usbd_q;
+static osal_queue_t _usbd_q;
// Mutex for claiming endpoint
#if OSAL_MUTEX_REQUIRED
- tu_static osal_mutex_def_t _ubsd_mutexdef;
- tu_static osal_mutex_t _usbd_mutex;
+ static osal_mutex_def_t _ubsd_mutexdef;
+ static osal_mutex_t _usbd_mutex;
#else
#define _usbd_mutex NULL
#endif
@@ -382,6 +411,18 @@ void usbd_control_set_request(tusb_control_request_t const *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 event, uint32_t xferred_bytes);
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) {
+ *driver_count = 0;
+ return NULL;
+}
+
+TU_ATTR_WEAK bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) {
+ (void) rhport; (void) ep_addr; (void) ff; (void) total_bytes;
+ return false;
+}
//--------------------------------------------------------------------+
// Debug
@@ -464,17 +505,36 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true; // skip if already initialized
}
TU_ASSERT(rh_init);
-
- TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport,
- rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+ char const* speed_str = 0;
+ switch (rh_init->speed) {
+ case TUSB_SPEED_HIGH:
+ speed_str = "High";
+ break;
+ case TUSB_SPEED_FULL:
+ speed_str = "Full";
+ break;
+ case TUSB_SPEED_LOW:
+ speed_str = "Low";
+ break;
+ case TUSB_SPEED_AUTO:
+ speed_str = "Auto";
+ break;
+ default:
+ break;
+ }
+ TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport, speed_str);
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t));
+#endif
tu_varclr(&_usbd_dev);
_usbd_queued_setup = 0;
+ osal_spin_init(&_usbd_spin);
+
#if OSAL_MUTEX_REQUIRED
// Init device mutex
_usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
@@ -486,9 +546,8 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
TU_ASSERT(_usbd_q);
// Get application driver if available
- if (usbd_app_driver_get_cb) {
- _app_driver = usbd_app_driver_get_cb(&_app_driver_count);
- }
+ _app_driver = usbd_app_driver_get_cb(&_app_driver_count);
+ TU_ASSERT(_app_driver_count + BUILTIN_DRIVER_COUNT <= UINT8_MAX);
// Init class drivers
for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
@@ -517,7 +576,7 @@ bool tud_deinit(uint8_t rhport) {
// Deinit device controller driver
dcd_int_disable(rhport);
dcd_disconnect(rhport);
- dcd_deinit(rhport);
+ TU_VERIFY(dcd_deinit(rhport));
// Deinit class drivers
for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
@@ -539,7 +598,6 @@ bool tud_deinit(uint8_t rhport) {
#endif
_usbd_rhport = RHPORT_INVALID;
-
return true;
}
@@ -551,8 +609,8 @@ static void configuration_reset(uint8_t rhport) {
}
tu_varclr(&_usbd_dev);
- memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
- memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping
+ (void) memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
+ (void) memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping
}
static void usbd_reset(uint8_t rhport) {
@@ -583,12 +641,16 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
- if (!tud_inited()) return;
+ if (!tud_inited()) {
+ return;
+ }
// Loop until there is no more events in the queue
while (1) {
dcd_event_t event;
- if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return;
+ if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) {
+ return;
+ }
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup
@@ -612,7 +674,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
TU_ASSERT(_usbd_queued_setup > 0,);
_usbd_queued_setup--;
TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8);
- if (_usbd_queued_setup) {
+ if (_usbd_queued_setup != 0) {
TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n");
break;
}
@@ -648,8 +710,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
_usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
if (0 == epnum) {
- usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result,
- event.xfer_complete.len);
+ usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
} else {
usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]);
TU_ASSERT(driver,);
@@ -683,7 +744,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
case USBD_EVENT_FUNC_CALL:
TU_LOG_USBD("\r\n");
- if (event.func_call.func) {
+ if (event.func_call.func != NULL) {
event.func_call.func(event.func_call.param);
}
break;
@@ -737,7 +798,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
}
#endif
- switch ( p_request->bmRequestType_bit.recipient ) {
+ switch (p_request->bmRequestType_bit.recipient) { //-V2520
//------------- Device Requests e.g in enumeration -------------//
case TUSB_REQ_RCPT_DEVICE:
if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) {
@@ -751,13 +812,13 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
return invoke_class_control(rhport, driver, p_request);
}
- if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
+ if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) {
// Non-standard request is not supported
TU_BREAKPOINT();
return false;
}
- switch ( p_request->bRequest ) {
+ switch (p_request->bRequest) { //-V2520
case TUSB_REQ_SET_ADDRESS:
// Depending on mcu, status phase could be sent either before or after changing device address,
// or even require stack to not response with status at all
@@ -779,18 +840,15 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// Only process if new configure is different
if (_usbd_dev.cfg_num != cfg_num) {
- if ( _usbd_dev.cfg_num ) {
+ if (_usbd_dev.cfg_num != 0) {
// already configured: need to clear all endpoints and driver first
TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
- // disable SOF
dcd_sof_enable(rhport, false);
-
- // close all non-control endpoints, cancel all pending transfers if any
dcd_edpt_close_all(rhport);
// close all drivers and current configured state except bus speed
- uint8_t const speed = _usbd_dev.speed;
+ const uint8_t speed = _usbd_dev.speed;
configuration_reset(rhport);
_usbd_dev.speed = speed; // restore speed
@@ -798,18 +856,15 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
_usbd_dev.cfg_num = cfg_num;
- // Handle the new configuration and execute the corresponding callback
- if ( cfg_num ) {
- // switch to new configuration if not zero
+ // Handle the new configuration
+ if (cfg_num == 0) {
+ tud_umount_cb();
+ } else {
if (!process_set_config(rhport, cfg_num)) {
- TU_MESS_FAILED();
- TU_BREAKPOINT();
_usbd_dev.cfg_num = 0;
- return false;
+ TU_ASSERT(false);
}
tud_mount_cb();
- } else {
- tud_umount_cb();
}
}
@@ -818,17 +873,17 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
break;
case TUSB_REQ_GET_DESCRIPTOR:
- TU_VERIFY( process_get_descriptor(rhport, p_request) );
+ TU_VERIFY(process_get_descriptor(rhport, p_request));
break;
case TUSB_REQ_SET_FEATURE:
- switch(p_request->wValue) {
+ switch(p_request->wValue) { //-V2520
case TUSB_REQ_FEATURE_REMOTE_WAKEUP:
TU_LOG_USBD(" Enable Remote Wakeup\r\n");
// Host may enable remote wake up before suspending especially HID device
_usbd_dev.remote_wakeup_en = true;
tud_control_status(rhport, p_request);
- break;
+ break;
#if CFG_TUD_TEST_MODE
case TUSB_REQ_FEATURE_TEST_MODE: {
@@ -842,7 +897,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
tud_control_status(rhport, p_request);
break;
}
- #endif /* CFG_TUD_TEST_MODE */
+ #endif
// Stall unsupported feature selector
default: return false;
@@ -852,13 +907,12 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
case TUSB_REQ_CLEAR_FEATURE:
// Only support remote wakeup for device feature
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
-
TU_LOG_USBD(" Disable Remote Wakeup\r\n");
// Host may disable remote wake up after resuming
_usbd_dev.remote_wakeup_en = false;
tud_control_status(rhport, p_request);
- break;
+ break;
case TUSB_REQ_GET_STATUS: {
// Device status bit mask
@@ -884,24 +938,24 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// all requests to Interface (STD or Class) is forwarded to class driver.
// notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE
- if ( !invoke_class_control(rhport, driver, p_request) ) {
+ if (!invoke_class_control(rhport, driver, p_request)) {
// For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class
// driver doesn't use alternate settings or implement this
TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
- switch(p_request->bRequest) {
- case TUSB_REQ_GET_INTERFACE:
- case TUSB_REQ_SET_INTERFACE:
- // Clear complete callback if driver set since it can also stall the request.
- usbd_control_set_complete_callback(NULL);
+ // Clear complete callback if driver set since it can also stall the request.
+ usbd_control_set_complete_callback(NULL);
- if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) {
- uint8_t alternate = 0;
- tud_control_xfer(rhport, p_request, &alternate, 1);
- }else {
- tud_control_status(rhport, p_request);
- }
- break;
+ switch (p_request->bRequest) { //-V2520
+ case TUSB_REQ_GET_INTERFACE: {
+ uint8_t alternate = 0;
+ tud_control_xfer(rhport, p_request, &alternate, 1);
+ break;
+ }
+
+ case TUSB_REQ_SET_INTERFACE:
+ tud_control_status(rhport, p_request);
+ break;
default: return false;
}
@@ -918,15 +972,15 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) );
usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]);
- if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
+ if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) {
// Forward class request to its driver
TU_VERIFY(driver);
return invoke_class_control(rhport, driver, p_request);
} else {
// Handle STD request to endpoint
- switch ( p_request->bRequest ) {
+ switch (p_request->bRequest) { //-V2520
case TUSB_REQ_GET_STATUS: {
- uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000;
+ uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001u : 0x0000u;
tud_control_xfer(rhport, p_request, &status, 2);
}
break;
@@ -941,7 +995,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
}
}
- if (driver) {
+ if (driver != NULL) {
// Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request
// We will also forward std request targeted endpoint to class drivers as well
@@ -951,7 +1005,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
usbd_control_set_complete_callback(NULL);
// skip ZLP status if driver already did that
- if ( !_usbd_dev.ep_status[0][TUSB_DIR_IN].busy ) tud_control_status(rhport, p_request);
+ if (!_usbd_dev.ep_status[0][TUSB_DIR_IN].busy) {
+ tud_control_status(rhport, p_request);
+ }
}
}
break;
@@ -962,8 +1018,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
return false;
}
}
+ break;
}
- break;
// Unknown recipient
default:
@@ -1026,10 +1082,11 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
// Some drivers use 2 or more interfaces but may not have IAD e.g MIDI (always) or
// BTH (even CDC) with class in device descriptor (single interface)
- if ( assoc_itf_count == 1)
- {
+ if (assoc_itf_count == 1) {
#if CFG_TUD_CDC
- if ( driver->open == cdcd_open ) assoc_itf_count = 2;
+ if ( driver->open == cdcd_open ) {
+ assoc_itf_count = 2;
+ }
#endif
#if CFG_TUD_MIDI
@@ -1046,6 +1103,28 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
#if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT
if ( driver->open == btd_open ) assoc_itf_count = 2;
#endif
+
+ #if CFG_TUD_AUDIO
+ if (driver->open == audiod_open) {
+ // UAC1 device doesn't have IAD, needs to read AS interface count from CS AC descriptor
+ if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
+ uint8_t const* p = tu_desc_next(p_desc);
+ uint8_t const* const itf_end = p_desc + remaining_len;
+ while (p < itf_end) {
+ if (TUSB_DESC_CS_INTERFACE == tu_desc_type(p) &&
+ AUDIO10_CS_AC_INTERFACE_HEADER == ((audio10_desc_cs_ac_interface_1_t const *) p)->bDescriptorSubType) {
+ audio10_desc_cs_ac_interface_1_t const * p_header = (audio10_desc_cs_ac_interface_1_t const *) p;
+ // AC + AS interfaces
+ assoc_itf_count = p_header->bInCollection + 1;
+ break;
+ }
+ p = tu_desc_next(p);
+ }
+ }
+ }
+ #endif
}
// bind (associated) interfaces to found driver
@@ -1081,8 +1160,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low( p_request->wValue );
- switch(desc_type)
- {
+ switch(desc_type) { //-V2520
case TUSB_DESC_DEVICE: {
TU_LOG_USBD(" Device\r\n");
@@ -1110,7 +1188,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
// requested by host if USB > 2.0 ( i.e 2.1 or 3.x )
uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb();
- TU_VERIFY(desc_bos);
+ TU_VERIFY(desc_bos != 0);
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) );
@@ -1126,12 +1204,12 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
if ( desc_type == TUSB_DESC_CONFIGURATION ) {
TU_LOG_USBD(" Configuration[%u]\r\n", desc_index);
desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index);
- TU_ASSERT(desc_config);
+ TU_ASSERT(desc_config != 0);
}else {
// Host only request this after getting Device Qualifier descriptor
TU_LOG_USBD(" Other Speed Configuration\r\n");
desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index);
- TU_VERIFY(desc_config);
+ TU_VERIFY(desc_config != 0);
}
// Use offsetof to avoid pointer to the odd/misaligned address
@@ -1141,8 +1219,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
}
// break; // unreachable
- case TUSB_DESC_STRING:
- {
+ case TUSB_DESC_STRING: {
TU_LOG_USBD(" String[%u]\r\n", desc_index);
// String Descriptor always uses the desc set from user
@@ -1228,6 +1305,32 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
send = true;
break;
+ case DCD_EVENT_XFER_COMPLETE: {
+ // Invoke the class callback associated with the endpoint address
+ uint8_t const ep_addr = event->xfer_complete.ep_addr;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+
+ send = true;
+ if(epnum > 0) {
+ usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]);
+
+ if (driver && driver->xfer_isr) {
+ _usbd_dev.ep_status[epnum][ep_dir].busy = 0;
+ _usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
+
+ send = !driver->xfer_isr(event->rhport, ep_addr, (xfer_result_t) event->xfer_complete.result, event->xfer_complete.len);
+
+ // xfer_isr() is deferred to xfer_cb(), revert busy/claimed status
+ if (send) {
+ _usbd_dev.ep_status[epnum][ep_dir].busy = 1;
+ _usbd_dev.ep_status[epnum][ep_dir].claimed = 1;
+ }
+ }
+ }
+ break;
+ }
+
default:
send = true;
break;
@@ -1242,17 +1345,21 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
// USBD API For Class Driver
//--------------------------------------------------------------------+
-void usbd_int_set(bool enabled)
-{
- if (enabled)
- {
+void usbd_int_set(bool enabled) {
+ if (enabled) {
dcd_int_enable(_usbd_rhport);
- }else
- {
+ } else {
dcd_int_disable(_usbd_rhport);
}
}
+void usbd_spin_lock(bool in_isr) {
+ osal_spin_lock(&_usbd_spin, in_isr);
+}
+void usbd_spin_unlock(bool in_isr) {
+ osal_spin_unlock(&_usbd_spin, in_isr);
+}
+
// Parse consecutive endpoint descriptors (IN & OUT)
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)
{
diff --git a/src/device/usbd.h b/src/device/usbd.h
index de6007fb3..c446638c3 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBD_H_
-#define _TUSB_USBD_H_
+#ifndef TUSB_USBD_H_
+#define TUSB_USBD_H_
#include "common/tusb_common.h"
@@ -95,7 +95,9 @@ 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();
+ const bool is_mounted = tud_mounted();
+ const bool is_suspended = tud_suspended();
+ return is_mounted && !is_suspended;
}
// Remote wake up host, only if suspended and enabled by host
@@ -244,7 +246,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
/* 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), 16,\
+ 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 */\
@@ -270,6 +272,25 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
//--------------------------------------------------------------------+
+// 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
//--------------------------------------------------------------------+
@@ -280,7 +301,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// 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,\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, TUSB_CLASS_HID, (uint8_t)((_boot_protocol != HID_ITF_PROTOCOL_NONE) ? (uint8_t)HID_SUBCLASS_BOOT : 0u), _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 */\
@@ -293,7 +314,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// 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,\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_HID, (uint8_t)((_boot_protocol != HID_ITF_PROTOCOL_NONE) ? (uint8_t)HID_SUBCLASS_BOOT : 0u), _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 */\
@@ -311,7 +332,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
/* 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),\
+ 9, TUSB_DESC_CS_INTERFACE, AUDIO10_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 */\
@@ -364,242 +385,352 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
TUD_MIDI_JACKID_OUT_EMB(1)
//--------------------------------------------------------------------+
-// Audio v2.0 Descriptor Templates
+// Audio Descriptor Templates
//--------------------------------------------------------------------+
+
+/* Audio v1.0 Descriptor Templates */
+
+/* Standard AC Interface Descriptor UAC1 (4.3.1) */
+#define TUD_AUDIO10_DESC_STD_AC_LEN 9
+#define TUD_AUDIO10_DESC_STD_AC(_itfnum, _nEPs, _stridx) \
+ TUD_AUDIO10_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V1, _stridx
+
+/* Class-Specific AC Interface Header Descriptor UAC1 (4.3.2) */
+#define TUD_AUDIO10_DESC_CS_AC_LEN(_nintfs) (8 + (_nintfs))
+// Class-Specific AC Interface Header descriptor, take list of streaming interface numbers as variable arguments
+#define TUD_AUDIO10_DESC_CS_AC(_bcdADC, _totallen, ...) \
+ TUD_AUDIO10_DESC_CS_AC_LEN(TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), U16_TO_U8S_LE(_totallen + TUD_AUDIO10_DESC_CS_AC_LEN(TU_ARGS_NUM(__VA_ARGS__))), TU_ARGS_NUM(__VA_ARGS__), __VA_ARGS__
+
+/* Input Terminal Descriptor UAC1 (4.3.2.1) */
+#define TUD_AUDIO10_DESC_INPUT_TERM_LEN 12
+#define TUD_AUDIO10_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _nchannels, _channelcfg, _idxchannelnames, _stridx) \
+ TUD_AUDIO10_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _nchannels, U16_TO_U8S_LE(_channelcfg), _idxchannelnames, _stridx
+
+/* Output Terminal Descriptor UAC1 (4.3.2.2) */
+#define TUD_AUDIO10_DESC_OUTPUT_TERM_LEN 9
+#define TUD_AUDIO10_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _stridx) \
+ TUD_AUDIO10_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _stridx
+
+/* Mixer Unit Descriptor UAC1 (4.3.2.3) - One Input Pin */
+#define TUD_AUDIO10_DESC_MIXER_UNIT_ONE_PIN_LEN(_ctrlsize) (11 + (_ctrlsize))
+#define TUD_AUDIO10_DESC_MIXER_UNIT_ONE_PIN(_unitid, _srcid, _nrchannels, _channelcfg, _idxchannelnames, _ctrlsize, _stridx, ...) \
+ TUD_AUDIO10_DESC_MIXER_UNIT_ONE_PIN_LEN(_ctrlsize), TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_MIXER_UNIT, _unitid, 1, _srcid, _nrchannels, U16_TO_U8S_LE(_channelcfg), _idxchannelnames, __VA_ARGS__, _stridx
+
+/* Selector Unit Descriptor UAC1 (4.3.2.4) - One Input Pin */
+#define TUD_AUDIO10_DESC_SELECTOR_UNIT_ONE_PIN_LEN 7
+#define TUD_AUDIO10_DESC_SELECTOR_UNIT_ONE_PIN(_unitid, _srcid, _stridx) \
+ TUD_AUDIO10_DESC_SELECTOR_UNIT_ONE_PIN_LEN, TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_SELECTOR_UNIT, _unitid, 1, _srcid, _stridx
+
+/* Feature Unit Descriptor UAC1 (4.3.2.5) - Variable Channels */
+#define TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(_nchannels) (7 + ((_nchannels) + 1) * 2)
+// Feature Unit descriptor, take list of control bitmaps for master channel + each channel as variable arguments
+#define TUD_AUDIO10_DESC_FEATURE_UNIT(_unitid, _srcid, _stridx, ...) \
+ TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(TU_ARGS_NUM(__VA_ARGS__) - 1), TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, 2, TU_ARGS_APPLY_EXPAND(U16_TO_U8S_LE, __VA_ARGS__), _stridx
+
+/* Standard AS Interface Descriptor UAC1 (4.5.1) */
+#define TUD_AUDIO10_DESC_STD_AS_LEN 9
+#define TUD_AUDIO10_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
+ TUD_AUDIO10_DESC_STD_AS_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V1, _stridx
+
+/* Class-Specific AS Interface Descriptor UAC1 (4.5.2) */
+#define TUD_AUDIO10_DESC_CS_AS_INT_LEN 7
+#define TUD_AUDIO10_DESC_CS_AS_INT(_termid, _delay, _formattype) \
+ TUD_AUDIO10_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AS_INTERFACE_AS_GENERAL, _termid, _delay, U16_TO_U8S_LE(_formattype)
+
+/* Type I Format Type Descriptor UAC1 (2.2.5) */
+#define TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs) (8 + (_nfreqs)*3)
+// Type I Format descriptor, take list of sample rates in Hz as variable arguments
+#define TUD_AUDIO10_DESC_TYPE_I_FORMAT(_nrchannels, _subframesize, _bitresolution, ...) \
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO10_FORMAT_TYPE_I, _nrchannels, _subframesize, _bitresolution, TU_ARGS_NUM(__VA_ARGS__), TU_ARGS_APPLY_EXPAND(U24_TO_U8S_LE, __VA_ARGS__)
+
+/* Standard AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.1) */
+#define TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN 9
+#define TUD_AUDIO10_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval, _sync_ep) \
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval, 0x00, _sync_ep
+
+/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor UAC1 (4.6.1.2) */
+#define TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN 7
+#define TUD_AUDIO10_DESC_CS_AS_ISO_EP(_attr, _lockdelayunits, _lockdelay) \
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO10_CS_EP_SUBTYPE_GENERAL, _attr, _lockdelayunits, U16_TO_U8S_LE(_lockdelay)
+
+/* Standard AS Isochronous Synch Endpoint Descriptor UAC1 (4.6.2.1) */
+#define TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP_LEN 9
+#define TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP(_ep, _bRefresh) \
+ TUD_AUDIO10_DESC_STD_AS_ISO_SYNC_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(3), 1, _bRefresh, 0x00
+
+/* Standard AC Interrupt Endpoint Descriptor UAC1 (4.4.2) */
+#define TUD_AUDIO10_DESC_STD_AC_INT_EP_LEN 9
+#define TUD_AUDIO10_DESC_STD_AC_INT_EP(_ep, _interval) \
+ TUD_AUDIO10_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(2), _interval, 0x00, 0x00
+
+// AUDIO simple descriptor templates for UAC1
+
+// AUDIO simple descriptor (UAC1) for 1 microphone input
+// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal
+
+#define TUD_AUDIO10_MIC_ONE_CH_DESC_LEN(_nfreqs) (\
+ + TUD_AUDIO10_DESC_STD_AC_LEN\
+ + TUD_AUDIO10_DESC_CS_AC_LEN(1)\
+ + TUD_AUDIO10_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(1)\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_STD_AS_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO10_DESC_TYPE_I_FORMAT_LEN(_nfreqs)\
+ + TUD_AUDIO10_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO10_DESC_CS_AS_ISO_EP_LEN)
+
+#define TUD_AUDIO10_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize, ...) \
+ /* Standard AC Interface Descriptor(4.3.1) */\
+ TUD_AUDIO10_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Class-Specific AC Interface Header Descriptor(4.3.2) */\
+ TUD_AUDIO10_DESC_CS_AC(/*_bcdADC*/ 0x0100, /*_totallen*/ (TUD_AUDIO10_DESC_INPUT_TERM_LEN+TUD_AUDIO10_DESC_OUTPUT_TERM_LEN+TUD_AUDIO10_DESC_FEATURE_UNIT_LEN(1)), /*_itf*/ ((_itfnum)+1)),\
+ /* Input Terminal Descriptor(4.3.2.1) */\
+ TUD_AUDIO10_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_nchannels*/ 0x01, /*_channelcfg*/ AUDIO10_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_stridx*/ 0x00),\
+ /* Output Terminal Descriptor(4.3.2.2) */\
+ TUD_AUDIO10_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_stridx*/ 0x00),\
+ /* Feature Unit Descriptor(4.3.2.5) */\
+ TUD_AUDIO10_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlmaster*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME), /*_ctrlch1*/ (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME)),\
+ /* Standard AS Interface Descriptor(4.5.1) */\
+ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
+ /* Standard AS Interface Descriptor(4.5.1) */\
+ /* Interface 1, Alternate 1 - alternate interface for data streaming */\
+ TUD_AUDIO10_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
+ /* Class-Specific AS Interface Descriptor(4.5.2) */\
+ TUD_AUDIO10_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_delay*/ 0x01, /*_formattype*/ AUDIO10_DATA_FORMAT_TYPE_I_PCM),\
+ /* Type I Format Type Descriptor(2.2.5) */\
+ TUD_AUDIO10_DESC_TYPE_I_FORMAT(/*_nrchannels*/ 0x01, /*_subframesize*/ _nBytesPerSample, /*_bitresolution*/ _nBitsUsedPerSample, /*_freq*/ __VA_ARGS__),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.6.1.1) */\
+ TUD_AUDIO10_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01, /* _sync_ep */ 0x00),\
+ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.6.1.2) */\
+ TUD_AUDIO10_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO10_CS_AS_ISO_DATA_EP_ATT_SAMPLING_FRQ, /*_lockdelayunits*/ AUDIO10_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001)
+
+/* 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
+#define TUD_AUDIO20_DESC_IAD_LEN 8
+#define TUD_AUDIO20_DESC_IAD(_firstitf, _nitfs, _stridx) \
+ TUD_AUDIO20_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
+#define TUD_AUDIO20_DESC_STD_AC_LEN 9
+#define TUD_AUDIO20_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\
+ TUD_AUDIO20_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
+#define TUD_AUDIO20_DESC_CS_AC_LEN 9
+#define TUD_AUDIO20_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio20_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_AUDIO20_DESC_CS_AC_LEN, we already do this here*/ \
+ TUD_AUDIO20_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO20_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
+#define TUD_AUDIO20_DESC_CLK_SRC_LEN 8
+#define TUD_AUDIO20_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \
+ TUD_AUDIO20_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_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
+#define TUD_AUDIO20_DESC_INPUT_TERM_LEN 17
+#define TUD_AUDIO20_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \
+ TUD_AUDIO20_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_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
+#define TUD_AUDIO20_DESC_OUTPUT_TERM_LEN 12
+#define TUD_AUDIO20_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \
+ TUD_AUDIO20_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_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
+#define TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(_nchannels) (6 + ((_nchannels) + 1) * 4)
+#define TUD_AUDIO20_DESC_FEATURE_UNIT(_unitid, _srcid, _stridx, ...) \
+ TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(TU_ARGS_NUM(__VA_ARGS__) - 1), TUSB_DESC_CS_INTERFACE, AUDIO20_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, TU_ARGS_APPLY_EXPAND(U32_TO_U8S_LE, __VA_ARGS__), _stridx
/* 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
+#define TUD_AUDIO20_DESC_STD_AC_INT_EP_LEN 7
+#define TUD_AUDIO20_DESC_STD_AC_INT_EP(_ep, _interval) \
+ TUD_AUDIO20_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
+#define TUD_AUDIO20_DESC_STD_AS_LEN 9
+#define TUD_AUDIO20_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
+ TUD_AUDIO20_DESC_STD_AS_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
+#define TUD_AUDIO20_DESC_CS_AS_INT_LEN 16
+#define TUD_AUDIO20_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \
+ TUD_AUDIO20_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_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
+#define TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN 6
+#define TUD_AUDIO20_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_AUDIO20_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO20_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
+#define TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN 7
+#define TUD_AUDIO20_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \
+ TUD_AUDIO20_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)
+#define TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN 8
+#define TUD_AUDIO20_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO20_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
+#define TUD_AUDIO20_DESC_STD_AS_ISO_FB_EP_LEN 7
+#define TUD_AUDIO20_DESC_STD_AS_ISO_FB_EP(_ep, _epsize, _interval) \
+ TUD_AUDIO20_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_AUDIO20_MIC_ONE_CH_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1)\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
-#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces
+#define TUD_AUDIO20_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) \
+#define TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1), /*_ctrl*/ AUDIO20_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),\
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS),\
/* 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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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)
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_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_AUDIO20_MIC_FOUR_CH_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(4)\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN)
-#define TUD_AUDIO_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces
+#define TUD_AUDIO20_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) \
+#define TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(4), /*_ctrl*/ AUDIO20_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),\
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS),\
/* 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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_channelcfg*/ AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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)
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_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_AUDIO20_SPEAKER_MONO_FB_DESC_LEN (TUD_AUDIO20_DESC_IAD_LEN\
+ + TUD_AUDIO20_DESC_STD_AC_LEN\
+ + TUD_AUDIO20_DESC_CS_AC_LEN\
+ + TUD_AUDIO20_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO20_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1)\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO20_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_CS_AS_ISO_EP_LEN\
+ + TUD_AUDIO20_DESC_STD_AS_ISO_FB_EP_LEN)
-#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \
+#define TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN+TUD_AUDIO20_DESC_INPUT_TERM_LEN+TUD_AUDIO20_DESC_OUTPUT_TERM_LEN+TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(1), /*_ctrl*/ AUDIO20_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),\
+ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS),\
/* 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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_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),\
+ TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_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)
+ TUD_AUDIO20_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)
+#define TUD_AUDIO_EP_SIZE(_is_highspeed, _maxFrequency, _nBytesPerSample, _nChannels) \
+ (((((_maxFrequency) + ((_is_highspeed) ? 7999 : 999)) / ((_is_highspeed) ? 8000 : 1000)) + 1) * (_nBytesPerSample) * (_nChannels))
//--------------------------------------------------------------------+
@@ -678,44 +809,44 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
// 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), \
+ 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) \
+#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_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_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_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_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_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_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_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)
+#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
@@ -885,6 +1016,6 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
}
#endif
-#endif /* _TUSB_USBD_H_ */
+#endif /* TUSB_USBD_H_ */
/** @} */
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index c9700fd9d..2c2ff76b7 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -93,7 +93,7 @@ static bool data_stage_xact(uint8_t rhport) {
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr = EDPT_CTRL_IN;
- if (xact_len) {
+ if (0u != xact_len) {
TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
}
}
@@ -159,7 +159,7 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
// invoke optional dcd hook if available
dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
- if (_ctrl_xfer.complete_cb) {
+ if (NULL != _ctrl_xfer.complete_cb) {
// TODO refactor with usbd_driver_print_control_complete_name
_ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
}
@@ -185,7 +185,7 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
// 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 (NULL != _ctrl_xfer.complete_cb) {
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
#endif
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 190d6fd7f..6e220129a 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -57,17 +57,20 @@ typedef struct {
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) TU_ATTR_WEAK;
+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
@@ -114,20 +117,17 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endp
// 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);
+ const bool is_busy = usbd_edpt_busy(rhport, ep_addr);
+ const bool is_stalled = usbd_edpt_stalled(rhport, ep_addr);
+ return !is_busy && !is_stalled;
}
// 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
diff --git a/src/host/hcd.h b/src/host/hcd.h
index d3551bf5b..36a7f5da5 100644
--- a/src/host/hcd.h
+++ b/src/host/hcd.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_HCD_H_
-#define _TUSB_HCD_H_
+#ifndef TUSB_HCD_H_
+#define TUSB_HCD_H_
#include "common/tusb_common.h"
#include "osal/osal.h"
@@ -84,7 +84,7 @@ typedef struct {
// FUNC_CALL
struct {
- void (*func) (void*);
+ void (*func) (void* param);
void* param;
}func_call;
};
diff --git a/src/host/usbh.c b/src/host/usbh.c
index b7d5a05f2..734024771 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -88,32 +88,47 @@ TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_si
return false;
}
+TU_ATTR_WEAK usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count) {
+ *driver_count = 0;
+ return NULL;
+}
+
+TU_ATTR_WEAK void tuh_mount_cb(uint8_t daddr) {
+ (void) daddr;
+}
+
+TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr) {
+ (void) daddr;
+}
+
//--------------------------------------------------------------------+
// Data Structure
//--------------------------------------------------------------------+
typedef struct {
tuh_bus_info_t bus_info;
- // Device State
- struct TU_ATTR_PACKED {
- volatile uint8_t connected : 1; // After 1st transfer
- volatile uint8_t addressed : 1; // After SET_ADDR
- volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
- volatile uint8_t suspended : 1; // Bus suspended
- // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
- };
-
// Device Descriptor
- uint8_t ep0_size;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
+ uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
- // Configuration Descriptor
- // uint8_t interface_count; // bNumInterfaces alias
+ // Device State
+ struct TU_ATTR_PACKED {
+ volatile uint8_t connected : 1; // After 1st transfer
+ volatile uint8_t addressed : 1; // After SET_ADDR
+ volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
+ volatile uint8_t suspended : 1; // Bus suspended
+ // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
+ };
// Endpoint & Interface
uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
@@ -147,6 +162,9 @@ static osal_mutex_t _usbh_mutex;
#define _usbh_mutex NULL
#endif
+// Spinlock for interrupt handler
+static OSAL_SPINLOCK_DEF(_usbh_spin, usbh_int_set);
+
// Event queue: usbh_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
@@ -274,13 +292,17 @@ static uint8_t _app_driver_count = 0;
#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
-static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
+// virtually joins built-in and application drivers together.
+// Application is positioned first to allow overwriting built-in ones.
+TU_ATTR_ALWAYS_INLINE static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
usbh_class_driver_t const *driver = NULL;
-
- if ( drv_id < _app_driver_count ) {
+ if (drv_id < _app_driver_count) {
driver = &_app_driver[drv_id];
- } else if ( drv_id < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0) {
- driver = &usbh_class_drivers[drv_id - _app_driver_count];
+ } else {
+ drv_id -= _app_driver_count;
+ if (drv_id < BUILTIN_DRIVER_COUNT) {
+ driver = &usbh_class_drivers[drv_id];
+ }
}
return driver;
@@ -300,7 +322,7 @@ TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr)
}
TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr(uint8_t daddr) {
- return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
+ return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX); //-V560
}
TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
@@ -354,7 +376,8 @@ bool tuh_connected(uint8_t daddr) {
return _usbh_data.enumerating_daddr == 0;
} else {
const usbh_device_t* dev = get_device(daddr);
- return dev && dev->connected;
+ TU_VERIFY(dev != NULL);
+ return dev->connected;
}
}
@@ -370,10 +393,32 @@ bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
return true;
}
+bool tuh_descriptor_get_device_local(uint8_t daddr, tusb_desc_device_t* desc_device) {
+ usbh_device_t *dev = get_device(daddr);
+ TU_VERIFY(dev && desc_device);
+
+ desc_device->bLength = sizeof(tusb_desc_device_t);
+ desc_device->bDescriptorType = TUSB_DESC_DEVICE;
+ desc_device->bcdUSB = dev->bcdUSB;
+ desc_device->bDeviceClass = dev->bDeviceClass;
+ desc_device->bDeviceSubClass = dev->bDeviceSubClass;
+ desc_device->bDeviceProtocol = dev->bDeviceProtocol;
+ desc_device->bMaxPacketSize0 = dev->bMaxPacketSize0;
+ desc_device->idVendor = dev->idVendor;
+ desc_device->idProduct = dev->idProduct;
+ desc_device->bcdDevice = dev->bcdDevice;
+ desc_device->iManufacturer = dev->iManufacturer;
+ desc_device->iProduct = dev->iProduct;
+ desc_device->iSerialNumber = dev->iSerialNumber;
+ desc_device->bNumConfigurations = dev->bNumConfigurations;
+
+ return true;
+}
+
tusb_speed_t tuh_speed_get(uint8_t daddr) {
tuh_bus_info_t bus_info;
tuh_bus_info_get(daddr, &bus_info);
- return bus_info.speed;
+ return (tusb_speed_t)bus_info.speed;
}
bool tuh_rhport_is_active(uint8_t rhport) {
@@ -399,8 +444,8 @@ bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
static void clear_device(usbh_device_t* dev) {
tu_memclr(dev, sizeof(usbh_device_t));
- memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
- memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
+ (void) memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
+ (void) memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
}
bool tuh_inited(void) {
@@ -411,9 +456,26 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
if (tuh_rhport_is_active(rhport)) {
return true; // skip if already initialized
}
-
- TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport,
- rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
+#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
+ char const* speed_str = 0;
+ switch (rh_init->speed) {
+ case TUSB_SPEED_HIGH:
+ speed_str = "High";
+ break;
+ case TUSB_SPEED_FULL:
+ speed_str = "Full";
+ break;
+ case TUSB_SPEED_LOW:
+ speed_str = "Low";
+ break;
+ case TUSB_SPEED_AUTO:
+ speed_str = "Auto";
+ break;
+ default:
+ break;
+ }
+ TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport, speed_str);
+#endif
// Init host stack if not already
if (!tuh_inited()) {
@@ -424,6 +486,8 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
TU_LOG_INT_USBH(sizeof(tu_fifo_t));
TU_LOG_INT_USBH(sizeof(tu_edpt_stream_t));
+ osal_spin_init(&_usbh_spin);
+
// Event queue
_usbh_q = osal_queue_create(&_usbh_qdef);
TU_ASSERT(_usbh_q != NULL);
@@ -435,9 +499,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
#endif
// Get application driver if available
- if (usbh_app_driver_get_cb) {
- _app_driver = usbh_app_driver_get_cb(&_app_driver_count);
- }
+ _app_driver = usbh_app_driver_get_cb(&_app_driver_count);
// Device
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
@@ -453,7 +515,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Class drivers
for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
usbh_class_driver_t const* driver = get_driver(drv_id);
- if (driver) {
+ if (driver != NULL) {
TU_LOG_USBH("%s init\r\n", driver->name);
driver->init();
}
@@ -600,12 +662,12 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
// with enabled driver e.g HID endpoint
#if CFG_TUH_API_EDPT_XFER
tuh_xfer_cb_t const complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb;
- if ( complete_cb ) {
+ if (complete_cb != NULL) {
// re-construct xfer info
tuh_xfer_t xfer = {
.daddr = event.dev_addr,
.ep_addr = ep_addr,
- .result = event.xfer_complete.result,
+ .result = (xfer_result_t)event.xfer_complete.result,
.actual_len = event.xfer_complete.len,
.buflen = 0, // not available
.buffer = NULL, // not available
@@ -618,7 +680,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
{
uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
usbh_class_driver_t const* driver = get_driver(drv_id);
- if (driver) {
+ if (driver != NULL) {
TU_LOG_USBH(" %s xfer callback\r\n", driver->name);
driver->xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
event.xfer_complete.len);
@@ -633,10 +695,13 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
}
case USBH_EVENT_FUNC_CALL:
- if (event.func_call.func) event.func_call.func(event.func_call.param);
+ if (event.func_call.func != NULL) {
+ event.func_call.func(event.func_call.param);
+ }
break;
default:
+ // unknown event
break;
}
@@ -686,7 +751,7 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) {
tu_str_std_request[xfer->setup->bRequest] : "Class Request");
TU_LOG_BUF_USBH(xfer->setup, 8);
- if (xfer->complete_cb) {
+ if (xfer->complete_cb != NULL) {
TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) &_usbh_epbuf.request));
}else {
// blocking if complete callback is not provided
@@ -738,7 +803,7 @@ static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
_control_set_xfer_stage(CONTROL_STAGE_IDLE);
- if (xfer_temp.complete_cb) {
+ if (xfer_temp.complete_cb != NULL) {
xfer_temp.complete_cb(&xfer_temp);
}
}
@@ -777,7 +842,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
case XFER_RESULT_SUCCESS:
switch(ctrl_info->stage) {
case CONTROL_STAGE_SETUP:
- if (request->wLength) {
+ if (request->wLength > 0) {
// DATA stage: initial data toggle is always 1
_control_set_xfer_stage(CONTROL_STAGE_DATA);
const uint8_t ep_data = tu_edpt_addr(0, request->bmRequestType_bit.direction);
@@ -786,18 +851,19 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
}
TU_ATTR_FALLTHROUGH;
- case CONTROL_STAGE_DATA:
- if (request->wLength) {
- TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
- TU_LOG_MEM_USBH(ctrl_info->buffer, xferred_bytes, 2);
- }
- ctrl_info->actual_len = (uint16_t) xferred_bytes;
+ case CONTROL_STAGE_DATA: {
+ if (request->wLength > 0) {
+ TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
+ TU_LOG_MEM_USBH(ctrl_info->buffer, xferred_bytes, 2);
+ }
+ ctrl_info->actual_len = (uint16_t) xferred_bytes;
- // ACK stage: toggle is always 1
- _control_set_xfer_stage(CONTROL_STAGE_ACK);
- const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction);
- TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0));
- break;
+ // ACK stage: toggle is always 1
+ _control_set_xfer_stage(CONTROL_STAGE_ACK);
+ const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction);
+ TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0));
+ break;
+ }
case CONTROL_STAGE_ACK: {
// Abort all pending transfers if SET_CONFIGURATION request
@@ -895,6 +961,14 @@ void usbh_int_set(bool enabled) {
}
}
+void usbh_spin_lock(bool in_isr) {
+ osal_spin_lock(&_usbh_spin, in_isr);
+}
+
+void usbh_spin_unlock(bool in_isr) {
+ osal_spin_unlock(&_usbh_spin, in_isr);
+}
+
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr) {
hcd_event_t event = { 0 };
event.event_id = USBH_EVENT_FUNC_CALL;
@@ -1018,7 +1092,7 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) {
bool tuh_bus_info_get(uint8_t daddr, tuh_bus_info_t* bus_info) {
usbh_device_t const* dev = get_device(daddr);
- if (dev) {
+ if (dev != NULL) {
*bus_info = dev->bus_info;
} else {
*bus_info = _usbh_data.dev0_bus;
@@ -1043,7 +1117,9 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
}
break;
- default: break;
+ default:
+ // nothing to do
+ break;
}
queue_event(event, in_isr);
@@ -1055,8 +1131,9 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
// generic helper to get a descriptor
// if blocking, user_data is pointed to xfer_result
-static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+TU_ATTR_ALWAYS_INLINE static inline
+bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_DEVICE,
@@ -1097,7 +1174,6 @@ bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer
}
//------------- String Descriptor -------------//
-
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) {
return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data);
@@ -1236,47 +1312,6 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
}
//--------------------------------------------------------------------+
-// Descriptor Sync
-//--------------------------------------------------------------------+
-
-#define _CONTROL_SYNC_API(_async_func, ...) \
- xfer_result_t result = XFER_RESULT_INVALID;\
- TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
- return (uint8_t) result
-
-uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
-}
-
-uint8_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) {
- _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
-}
-
-uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len) {
- _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
-}
-
-//--------------------------------------------------------------------+
// Detaching
//--------------------------------------------------------------------+
// a device unplugged from rhport:hub_addr:hub_port
@@ -1289,7 +1324,7 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
do {
for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
usbh_device_t* dev = &_usbh_devices[dev_id];
- uint8_t const daddr = dev_id + 1;
+ uint8_t const daddr = dev_id + 1u;
// hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
if (dev->bus_info.rhport == rhport && dev->connected &&
@@ -1305,15 +1340,13 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
#endif
{
// Invoke callback before closing driver (maybe call it later ?)
- if (tuh_umount_cb) {
- tuh_umount_cb(daddr);
- }
+ tuh_umount_cb(daddr);
}
// Close class driver
for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
usbh_class_driver_t const* driver = get_driver(drv_id);
- if (driver) {
+ if (driver != NULL) {
driver->close(daddr);
}
}
@@ -1331,7 +1364,7 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub
// find a marked hub to process
for (uint8_t h_id = 0; h_id < CFG_TUH_HUB; h_id++) {
- if (removing_hubs[h_id]) {
+ if (0 != removing_hubs[h_id]) {
removing_hubs[h_id] = 0;
// update hub_addr and hub_port for next loop
@@ -1400,16 +1433,13 @@ static bool enum_new_device(hcd_event_t* event) {
// wait until device connection is stable TODO non blocking
tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
- // clear roothub debouncing delay
- if (dev0_bus->hub_addr == 0) {
- _usbh_data.attach_debouncing_bm &= (uint8_t) ~TU_BIT(dev0_bus->rhport);
- }
-
if (dev0_bus->hub_addr == 0) {
// connected directly to roothub
// USB bus not active and frame number is not available yet.
// need to depend on tusb_time_millis_api() TODO non blocking
+ _usbh_data.attach_debouncing_bm &= (uint8_t) ~TU_BIT(dev0_bus->rhport); // clear roothub debouncing delay
+
if (!hcd_port_connect_status(dev0_bus->rhport)) {
TU_LOG_USBH("Device unplugged while debouncing\r\n");
enum_full_complete();
@@ -1480,7 +1510,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
usbh_device_t* dev = get_device(daddr);
tuh_bus_info_t* dev0_bus = &_usbh_data.dev0_bus;
if (daddr > 0) {
- TU_ASSERT(dev,);
+ TU_ASSERT(dev != NULL,);
}
uint16_t langid = 0x0409; // default is English
@@ -1490,7 +1520,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
hub_port_status_response_t port_status;
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
- if (!port_status.status.connection) {
+ if (0 == port_status.status.connection) {
TU_LOG_USBH("Device unplugged from hub while debouncing\r\n");
enum_full_complete();
return;
@@ -1512,7 +1542,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
hub_port_status_response_t port_status;
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
- if (port_status.change.reset) {
+ if (1 == port_status.change.reset) {
// Acknowledge Port Reset Change
TU_ASSERT(hub_port_clear_reset_change(dev0_bus->hub_addr, dev0_bus->hub_port, process_enumeration, ENUM_HUB_CLEAR_RESET_COMPLETE),);
} else {
@@ -1527,7 +1557,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
hub_port_status_response_t port_status;
hub_port_get_status_local(dev0_bus->hub_addr, dev0_bus->hub_port, &port_status);
- if (!port_status.status.connection) {
+ if (0 == port_status.status.connection) {
TU_LOG_USBH("Device unplugged from hub (not addressed yet)\r\n");
enum_full_complete();
return;
@@ -1566,7 +1596,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
usbh_device_t* new_dev = get_device(new_addr);
new_dev->bus_info = *dev0_bus;
new_dev->connected = 1;
- new_dev->ep0_size = desc_device->bMaxPacketSize0;
+ new_dev->bMaxPacketSize0 = desc_device->bMaxPacketSize0;
TU_ASSERT(tuh_address_set(0, new_addr, process_enumeration, ENUM_GET_DEVICE_DESC),);
break;
@@ -1583,7 +1613,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
usbh_device_close(dev0_bus->rhport, 0); // close dev0
- TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->ep0_size),); // open new control endpoint
+ TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->bMaxPacketSize0),); // open new control endpoint
TU_LOG_USBH("Get Device Descriptor\r\n");
TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_epbuf.ctrl, sizeof(tusb_desc_device_t),
@@ -1596,11 +1626,18 @@ static void process_enumeration(tuh_xfer_t* xfer) {
case ENUM_GET_STRING_LANGUAGE_ID_LEN: {
// save the received device descriptor
tusb_desc_device_t const *desc_device = (tusb_desc_device_t const *) _usbh_epbuf.ctrl;
- dev->idVendor = desc_device->idVendor;
- dev->idProduct = desc_device->idProduct;
- dev->iManufacturer = desc_device->iManufacturer;
- dev->iProduct = desc_device->iProduct;
- dev->iSerialNumber = desc_device->iSerialNumber;
+
+ dev->bcdUSB = desc_device->bcdUSB;
+ dev->bDeviceClass = desc_device->bDeviceClass;
+ dev->bDeviceSubClass = desc_device->bDeviceSubClass;
+ dev->bDeviceProtocol = desc_device->bDeviceProtocol;
+ dev->bMaxPacketSize0 = desc_device->bMaxPacketSize0;
+ dev->idVendor = desc_device->idVendor;
+ dev->idProduct = desc_device->idProduct;
+ dev->bcdDevice = desc_device->bcdDevice;
+ dev->iManufacturer = desc_device->iManufacturer;
+ dev->iProduct = desc_device->iProduct;
+ dev->iSerialNumber = desc_device->iSerialNumber;
dev->bNumConfigurations = desc_device->bNumConfigurations;
tuh_enum_descriptor_device_cb(daddr, desc_device); // callback
@@ -1625,9 +1662,8 @@ static void process_enumeration(tuh_xfer_t* xfer) {
tuh_descriptor_get_string(daddr, dev->iManufacturer, langid, _usbh_epbuf.ctrl, 2,
process_enumeration, ENUM_GET_STRING_MANUFACTURER);
break;
- }else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
}
case ENUM_GET_STRING_MANUFACTURER: {
@@ -1637,22 +1673,21 @@ static void process_enumeration(tuh_xfer_t* xfer) {
tuh_descriptor_get_string(daddr, dev->iManufacturer, langid, _usbh_epbuf.ctrl, str_len,
process_enumeration, ENUM_GET_STRING_PRODUCT_LEN);
break;
- } else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
}
- case ENUM_GET_STRING_PRODUCT_LEN:
+ case ENUM_GET_STRING_PRODUCT_LEN: {
if (dev->iProduct != 0) {
if (state == ENUM_GET_STRING_PRODUCT_LEN) {
langid = tu_le16toh(xfer->setup->wIndex); // get langid from previous setup packet if not fall through
}
- tuh_descriptor_get_string(daddr, dev->iProduct, langid, _usbh_epbuf.ctrl, 2,
- process_enumeration, ENUM_GET_STRING_PRODUCT);
+ tuh_descriptor_get_string(
+ daddr, dev->iProduct, langid, _usbh_epbuf.ctrl, 2, process_enumeration, ENUM_GET_STRING_PRODUCT);
break;
- } else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
+ }
case ENUM_GET_STRING_PRODUCT: {
if (dev->iProduct != 0) {
@@ -1661,22 +1696,21 @@ static void process_enumeration(tuh_xfer_t* xfer) {
tuh_descriptor_get_string(daddr, dev->iProduct, langid, _usbh_epbuf.ctrl, str_len,
process_enumeration, ENUM_GET_STRING_SERIAL_LEN);
break;
- } else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
}
- case ENUM_GET_STRING_SERIAL_LEN:
+ case ENUM_GET_STRING_SERIAL_LEN: {
if (dev->iSerialNumber != 0) {
if (state == ENUM_GET_STRING_SERIAL_LEN) {
langid = tu_le16toh(xfer->setup->wIndex); // get langid from previous setup packet if not fall through
}
- tuh_descriptor_get_string(daddr, dev->iSerialNumber, langid, _usbh_epbuf.ctrl, 2,
- process_enumeration, ENUM_GET_STRING_SERIAL);
+ tuh_descriptor_get_string(
+ daddr, dev->iSerialNumber, langid, _usbh_epbuf.ctrl, 2, process_enumeration, ENUM_GET_STRING_SERIAL);
break;
- } else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
+ }
case ENUM_GET_STRING_SERIAL: {
if (dev->iSerialNumber != 0) {
@@ -1685,9 +1719,8 @@ static void process_enumeration(tuh_xfer_t* xfer) {
tuh_descriptor_get_string(daddr, dev->iSerialNumber, langid, _usbh_epbuf.ctrl, str_len,
process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC);
break;
- } else {
- TU_ATTR_FALLTHROUGH;
}
+ TU_ATTR_FALLTHROUGH;
}
case ENUM_GET_9BYTE_CONFIG_DESC: {
@@ -1719,7 +1752,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
case ENUM_SET_CONFIG: {
uint8_t config_idx = (uint8_t) tu_le16toh(xfer->setup->wIndex);
if (tuh_enum_descriptor_configuration_cb(daddr, config_idx, (const tusb_desc_configuration_t*) _usbh_epbuf.ctrl)) {
- TU_ASSERT(tuh_configuration_set(daddr, config_idx+1, process_enumeration, ENUM_CONFIG_DRIVER),);
+ TU_ASSERT(tuh_configuration_set(daddr, config_idx+1u, process_enumeration, ENUM_CONFIG_DRIVER),);
} else {
config_idx++;
TU_ASSERT(config_idx < dev->bNumConfigurations,);
@@ -1765,7 +1798,7 @@ static uint8_t enum_get_new_address(bool is_hub) {
}
for (uint8_t idx = start; idx < end; idx++) {
- if (!_usbh_devices[idx].connected) {
+ if (0 == _usbh_devices[idx].connected) {
return (idx + 1);
}
}
@@ -1875,7 +1908,7 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) {
// with usbh_driver_set_config_complete()
uint8_t const drv_id = dev->itf2drv[itf_num];
usbh_class_driver_t const * driver = get_driver(drv_id);
- if (driver) {
+ if (driver != NULL) {
TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
driver->set_config(dev_addr, itf_num);
break;
@@ -1890,9 +1923,7 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) {
TU_LOG_USBH("HUB address = %u is mounted\r\n", dev_addr);
}else {
// Invoke callback if available
- if (tuh_mount_cb) {
- tuh_mount_cb(dev_addr);
- }
+ tuh_mount_cb(dev_addr);
}
}
}
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 6f34d8bb3..4b6747848 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBH_H_
-#define _TUSB_USBH_H_
+#ifndef TUSB_USBH_H_
+#define TUSB_USBH_H_
#ifdef __cplusplus
extern "C" {
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Endpoint Bulk size depending on host mx speed
-#define TUH_EPSIZE_BULK_MPS (TUD_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
+#define TUH_EPSIZE_BULK_MPS (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
// forward declaration
struct tuh_xfer_s;
@@ -123,13 +123,13 @@ void tuh_enum_descriptor_device_cb(uint8_t daddr, const tusb_desc_device_t *desc
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)
-TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
+void tuh_mount_cb (uint8_t daddr);
// Invoked when a device failed to mount during enumeration process
-// TU_ATTR_WEAK void tuh_mount_failed_cb (uint8_t daddr);
+// void tuh_mount_failed_cb (uint8_t daddr);
// Invoked when a device is unmounted (detached)
-TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
+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);
@@ -179,7 +179,7 @@ TU_ATTR_ALWAYS_INLINE static inline void tuh_task(void) {
// Check if there is pending events need processing by tuh_task()
bool tuh_task_event_ready(void);
-#ifndef _TUSB_HCD_H_
+#ifndef TUSB_HCD_H_
extern void hcd_int_handler(uint8_t rhport, bool in_isr);
#endif
@@ -204,6 +204,9 @@ bool tuh_rhport_reset_bus(uint8_t rhport, bool active);
// 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);
@@ -231,8 +234,18 @@ 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.
@@ -324,45 +337,54 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void*
//--------------------------------------------------------------------+
// Descriptors Synchronous (blocking)
+// Sync API which is blocking until transfer is complete.
+// return transfer result
//--------------------------------------------------------------------+
-// Sync (blocking) version of tuh_descriptor_get()
-// return transfer result
-uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len);
+// 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 (blocking) version of tuh_descriptor_get_device()
-// return transfer result
-uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_configuration()
-// return transfer result
-uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_hid_report()
-// return transfer result
-uint8_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);
+// 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 (blocking) version of tuh_descriptor_get_string()
-// return transfer result
-uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_string_langid()
-TU_ATTR_ALWAYS_INLINE static inline
-uint8_t tuh_descriptor_get_string_langid_sync(uint8_t daddr, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_manufacturer_string()
-// return transfer result
-uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_product_string()
-// return transfer result
-uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t 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 (blocking) version of tuh_descriptor_get_serial_string()
-// return transfer result
-uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t 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
}
diff --git a/src/host/usbh_pvt.h b/src/host/usbh_pvt.h
index 61b012493..d722bb7e8 100644
--- a/src/host/usbh_pvt.h
+++ b/src/host/usbh_pvt.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBH_PVT_H_
-#define _TUSB_USBH_PVT_H_
+#ifndef TUSB_USBH_PVT_H_
+#define TUSB_USBH_PVT_H_
#include "osal/osal.h"
#include "common/tusb_fifo.h"
@@ -58,7 +58,7 @@ typedef struct {
// 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) TU_ATTR_WEAK;
+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);
@@ -71,6 +71,9 @@ 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
//--------------------------------------------------------------------+
diff --git a/src/osal/osal.h b/src/osal/osal.h
index 38d45da44..44521620f 100644
--- a/src/osal/osal.h
+++ b/src/osal/osal.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_H_
-#define _TUSB_OSAL_H_
+#ifndef TUSB_OSAL_H_
+#define TUSB_OSAL_H_
#ifdef __cplusplus
extern "C" {
@@ -33,7 +33,7 @@
#include "common/tusb_common.h"
-typedef void (*osal_task_func_t)( void * );
+typedef void (*osal_task_func_t)(void* param);
// Timeout
#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately
@@ -71,15 +71,18 @@ typedef void (*osal_task_func_t)( void * );
#error OS is not supported yet
#endif
-//--------------------------------------------------------------------+
-// OSAL Porting API
-// Should be implemented as static inline function in osal_port.h header
-/*
+/*--------------------------------------------------------------------
+ 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
+ void osal_semaphore_reset(osal_semaphore_t sem_hdl);
osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
bool osal_mutex_delete(osal_mutex_t mutex_hdl)
@@ -91,11 +94,11 @@ typedef void (*osal_task_func_t)( void * );
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_ */
+#endif
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index a3a0f3a3f..9aeda4d01 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -42,20 +42,20 @@ extern "C" {
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
- typedef StaticSemaphore_t osal_semaphore_def_t;
- typedef StaticSemaphore_t osal_mutex_def_t;
+typedef StaticSemaphore_t osal_semaphore_def_t;
+typedef StaticSemaphore_t osal_mutex_def_t;
#else
- // not used therefore defined to smallest possible type to save space
- typedef uint8_t osal_semaphore_def_t;
- typedef uint8_t osal_mutex_def_t;
+
+// 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
-{
+typedef struct {
uint16_t depth;
uint16_t item_sz;
void* buf;
@@ -70,29 +70,27 @@ typedef struct
} osal_queue_def_t;
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
- #define _OSAL_Q_NAME(_name) .name = #_name
+ #define OSAL_Q_NAME(_name) .name = #_name
#else
- #define _OSAL_Q_NAME(_name)
+ #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) }
+ 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;
+ if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return portMAX_DELAY; }
+ if (msec == 0) { return 0; }
uint32_t ticks = pdMS_TO_TICKS(msec);
- // 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;
+ // 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;
}
@@ -102,12 +100,73 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
}
//--------------------------------------------------------------------+
-// Semaphore API
+// 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 == 0) {
+ (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 == 0) {
+ (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);
+ return xSemaphoreCreateBinaryStatic((StaticSemaphore_t*) semdef);
#else
(void) semdef;
return xSemaphoreCreateBinary();
@@ -115,24 +174,17 @@ TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
- vSemaphoreDelete(semd_hdl);
+ vSemaphoreDelete((SemaphoreHandle_t) 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 ) {
+ if (!in_isr) {
return xSemaphoreGive(sem_hdl) != 0;
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
-
-#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
- // not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7
- if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
-#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
-#endif
-
return res != 0;
}
}
@@ -148,7 +200,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t c
//--------------------------------------------------------------------+
// 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);
@@ -174,7 +225,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
-
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
osal_queue_t q;
@@ -201,19 +251,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, v
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
- if ( !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);
-
-#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
- // not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7 (IDF v5)
- if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
-#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
-#endif
-
return res != 0;
}
}
diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h
index 16def0d2a..6d51f8ec3 100644
--- a/src/osal/osal_mynewt.h
+++ b/src/osal/osal_mynewt.h
@@ -41,6 +41,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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;
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index 40e9bb83a..bc86dcb28 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -32,13 +32,31 @@ extern "C" {
#endif
//--------------------------------------------------------------------+
-// TASK API
+// Spinlock API
//--------------------------------------------------------------------+
+typedef struct {
+ void (* interrupt_set)(bool enabled);
+} osal_spinlock_t;
-#if CFG_TUH_ENABLED
-// currently only needed/available in host mode
-TU_ATTR_WEAK void osal_task_delay(uint32_t msec);
-#endif
+// 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
@@ -123,7 +141,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
#include "common/tusb_fifo.h"
typedef struct {
- void (* interrupt_set)(bool);
+ void (* interrupt_set)(bool enabled);
tu_fifo_t ff;
} osal_queue_def_t;
@@ -138,7 +156,7 @@ 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) {
- tu_fifo_clear(&qdef->ff);
+ (void) tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 315de0950..f5385071a 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -44,6 +44,27 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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;
@@ -60,8 +81,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t
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;
+ return sem_release(sem_hdl);
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
@@ -118,7 +138,7 @@ 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) {
critical_section_init(&qdef->critsec);
- tu_fifo_clear(&qdef->ff);
+ (void) tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h
index c27814835..a778f5425 100644
--- a/src/osal/osal_rtthread.h
+++ b/src/osal/osal_rtthread.h
@@ -43,6 +43,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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;
diff --git a/src/osal/osal_rtx4.h b/src/osal/osal_rtx4.h
index 35909e4d6..35860ddd5 100644
--- a/src/osal/osal_rtx4.h
+++ b/src/osal/osal_rtx4.h
@@ -57,6 +57,25 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t 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;
diff --git a/src/osal/osal_zephyr.h b/src/osal/osal_zephyr.h
index 8ecb13c6d..91f225f79 100644
--- a/src/osal/osal_zephyr.h
+++ b/src/osal/osal_zephyr.h
@@ -36,6 +36,35 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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;
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index bb33200f2..971dbd62e 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -28,9 +28,9 @@
#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
-#include <stdatomic.h>
#include "host/hcd.h"
#include "host/usbh.h"
+#include "host/usbh_pvt.h"
//--------------------------------------------------------------------+
//
@@ -233,7 +233,7 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
- atomic_flag busy; // busy transferring
+ bool busy_lock; // busy transferring
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@@ -327,7 +327,9 @@ TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data
}
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
- if ( _hcd_data.peraddr == data ) return; // no need to change address
+ if (_hcd_data.peraddr == data) {
+ return; // no need to change address
+ }
_hcd_data.peraddr = data;
reg_write(rhport, PERADDR_ADDR, data, in_isr);
@@ -373,7 +375,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint
static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
uint8_t hirq;
- uint8_t const reg = RCVVFIFO_ADDR;
+ const uint8_t reg = RCVVFIFO_ADDR;
max3421_spi_lock(rhport, in_isr);
@@ -389,7 +391,7 @@ static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool
//--------------------------------------------------------------------+
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
- uint8_t const is_out = 1-ep_dir;
+ const uint8_t is_out = 1-ep_dir;
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
// control endpoint is bi-direction (skip check)
@@ -727,8 +729,8 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
// 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) {
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@@ -744,8 +746,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_generic(rhport, ep, true, false);
}
@@ -781,8 +792,17 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_setup(rhport, ep, false);
}
@@ -848,8 +868,8 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
}
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
- uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
- uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
+ const uint8_t ep_dir = 1 - ep->hxfr_bm.is_out;
+ const uint8_t ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
// save data toggle
if (ep_dir) {
@@ -867,7 +887,9 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
xact_generic(rhport, next_ep, true, in_isr);
}else {
// no more pending
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
}
@@ -906,7 +928,9 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xact_generic(rhport, next_ep, true, in_isr);
} else {
// no more pending in this frame -> clear busy
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
return;
@@ -997,8 +1021,8 @@ void print_hirq(uint8_t hirq) {
// Interrupt handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
- if (!hirq) return;
-// print_hirq(hirq);
+ if (!hirq) { return; }
+ // print_hirq(hirq);
if (hirq & HIRQ_FRAME_IRQ) {
_hcd_data.frame_count++;
@@ -1017,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
- if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
- xact_generic(rhport, ep_retry, true, in_isr);
+ if (ep_retry != NULL) {
+ bool has_xfer = false;
+
+ usbh_spin_lock(in_isr);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(in_isr);
+
+ if (has_xfer) {
+ xact_generic(rhport, ep_retry, true, in_isr);
+ }
}
}
diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
index 34a8be3b6..6ed4be410 100644
--- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
+++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
@@ -806,6 +806,20 @@ void dcd_edpt_close_all(uint8_t rhport)
_ft9xx_reset_edpts();
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+
+
// 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)
{
@@ -1200,5 +1214,4 @@ void ft9xx_usbd_pm_ISR(void)
}
}
}
-
#endif
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
index 11ddb683f..40ad4cf82 100644
--- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -344,24 +344,21 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// 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];
+static bool edpt_open(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size, tusb_xfer_type_t xfer) {
+ (void)rhport;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ 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);
+ ep->max_packet_size = max_packet_size;
+
unsigned val = USB_ENDPT_EPCTLDIS_MASK;
- val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
+ 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;
@@ -375,8 +372,27 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
-void dcd_edpt_close_all(uint8_t rhport)
-{
+bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ return edpt_open(rhport, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer);
+}
+
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ return edpt_open(rhport, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ const unsigned epn = tu_edpt_number(ep_desc->bEndpointAddress);
+ const unsigned dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+
+ dcd_int_disable(rhport);
+ ep->max_packet_size = tu_edpt_packet_size(ep_desc);
+ dcd_int_enable(rhport);
+
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
dcd_int_disable(rhport);
for (unsigned i = 1; i < 16; ++i) {
@@ -399,26 +415,6 @@ void dcd_edpt_close_all(uint8_t rhport)
}
}
-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);
@@ -564,5 +560,4 @@ void dcd_int_handler(uint8_t rhport)
process_tokdne(rhport);
}
}
-
#endif
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index a716dc24c..8c5253eb9 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -65,15 +65,18 @@ bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
// ENDPTCTRL
enum {
+ ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field
+};
+
+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)
+ ENDPTCTRL_ENABLE = TU_BIT(7),
};
-enum {
- ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field
-};
+#define ENDPTCTRL_TYPE(_type) ((_type) << ENDPTCTRL_TYPE_POS)
+ #define ENDPTCTRL_RESET_MASK (ENDPTCTRL_TYPE(TUSB_XFER_BULK) | (ENDPTCTRL_TYPE(TUSB_XFER_BULK) << 16u))
// USBSTS, USBINTR
enum {
@@ -170,9 +173,7 @@ 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)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t ci_ep_count(const ci_hs_regs_t *dcd_reg) {
return dcd_reg->DCCPARAMS & DCCPARAMS_DEN_MASK;
}
@@ -191,9 +192,8 @@ static void bus_reset(uint8_t rhport)
// 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));
+ for (uint8_t i = 1; i < ep_count; i++) {
+ dcd_reg->ENDPTCTRL[i] = ENDPTCTRL_RESET_MASK;
}
//------------- Clear All Registers -------------//
@@ -239,7 +239,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
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;
@@ -311,26 +315,25 @@ void dcd_sof_enable(uint8_t rhport, bool en)
// 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);
+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;
+ 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;
+ 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;
+ const uint32_t bufend = p_qtd->buffer[0] + total_bytes;
+ for (uint8_t i = 1; i < 5; i++) {
+ const uint32_t next_page = tu_align4k(p_qtd->buffer[i - 1]) + 4096;
+ if (bufend <= next_page) {
+ break;
+ }
p_qtd->buffer[i] = next_page;
@@ -342,6 +345,35 @@ static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
//--------------------------------------------------------------------+
// DCD Endpoint Port
//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_write(volatile uint32_t *epctrl, uint8_t dir, uint32_t value) {
+ if (dir == TUSB_DIR_OUT) {
+ *epctrl = (*epctrl & 0xFFFF0000u) | value;
+ } else {
+ *epctrl = (*epctrl & 0x0000FFFFu) | (value << 16);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_mask(volatile uint32_t *epctrl, uint8_t dir, uint32_t and_mask,
+ uint32_t or_mask) {
+ uint32_t value = *epctrl;
+ if (and_mask != 0) {
+ value &= (dir == TUSB_DIR_OUT) ? and_mask : (and_mask << 16u);
+ }
+ if (or_mask != 0) {
+ value |= (dir == TUSB_DIR_OUT) ? or_mask : (or_mask << 16u);
+ }
+
+ *epctrl = value;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_set(volatile uint32_t *epctrl, uint8_t dir, uint32_t mask) {
+ ep_ctrl_mask(epctrl, dir, 0, mask);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_clear(volatile uint32_t *epctrl, uint8_t dir, uint32_t mask) {
+ ep_ctrl_mask(epctrl, dir, ~mask, 0);
+}
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
@@ -362,80 +394,91 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t 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));
+ 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];
+static void qhd_init(dcd_qhd_t *p_qhd, uint16_t max_packet_size, uint8_t iso_mult) {
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->max_packet_size = max_packet_size;
+ p_qhd->iso_mult = iso_mult;
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;
+bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *endpoint_desc) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ const uint8_t epnum = tu_edpt_number(endpoint_desc->bEndpointAddress);
+ const uint8_t dir = tu_edpt_dir(endpoint_desc->bEndpointAddress);
+ const uint8_t xfer_type = endpoint_desc->bmAttributes.xfer;
+ TU_ASSERT(epnum < ci_ep_count(dcd_reg));
- 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);
- }
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ qhd_init(p_qhd, tu_edpt_packet_size(endpoint_desc), 0u);
+
+ // EP Control
+ const uint32_t epctrl = ENDPTCTRL_TYPE(xfer_type) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
+ ep_ctrl_write(&dcd_reg->ENDPTCTRL[epnum], dir, epctrl);
return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
- // 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;
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+ TU_ASSERT(epnum < ci_ep_count(dcd_reg));
- 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));
- }
+ // EP Control: set type but not enabled yet
+ const uint32_t epctrl = ENDPTCTRL_TYPE(TUSB_XFER_ISOCHRONOUS) | ENDPTCTRL_TOGGLE_RESET;
+ ep_ctrl_write(&dcd_reg->ENDPTCTRL[epnum], dir, epctrl);
+
+ return true;
}
-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);
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ const uint8_t epnum = tu_edpt_number(desc_ep->bEndpointAddress);
+ const uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
+ TU_ASSERT(epnum < ci_ep_count(dcd_reg));
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir];
+ volatile uint32_t *endptctrl = &dcd_reg->ENDPTCTRL[epnum];
- _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
+ // _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
+ // dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
// Flush EP
- uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
- dcd_reg->ENDPTFLUSH = flush_mask;
- while(dcd_reg->ENDPTFLUSH & flush_mask);
+ const uint32_t flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
+ dcd_reg->ENDPTFLUSH = flush_mask;
+ while (dcd_reg->ENDPTFLUSH & flush_mask) {}
+
+ // disable to change max packet size
+ ep_ctrl_clear(endptctrl, dir, ENDPTCTRL_ENABLE);
+
+ qhd_init(p_qhd, tu_edpt_packet_size(desc_ep), 1u);
+
+ ep_ctrl_set(endptctrl, dir, ENDPTCTRL_ENABLE);
+
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+ ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport);
- // Clear EP enable
- dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0));
+ // 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] = ENDPTCTRL_RESET_MASK;
+ }
}
static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
@@ -674,5 +717,4 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_sof(rhport, frame, true);
}
}
-
#endif
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index 56ecb7575..868a10dd9 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -1025,6 +1025,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
tu_memclr(xfer, sizeof(*xfer));
}
+#if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false;
+}
+#endif
+
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);
@@ -1218,5 +1233,4 @@ void dcd_int_handler(uint8_t rhport)
handle_alt_ev();
}
}
-
#endif
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index da9f49d29..c33c970e4 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -35,9 +35,15 @@
#include "host/hcd.h"
#include "host/usbh.h"
+#include "host/usbh_pvt.h"
#include "ehci_api.h"
#include "ehci.h"
+// NXP specific fixes
+#if TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX, OPT_MCU_LPC55, OPT_MCU_MCXN9)
+#include "fsl_device_registers.h"
+#endif
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -179,12 +185,43 @@ static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) {
}
}
+#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U))
+static void nxp_usbphy_disconn_detector_set(uint8_t port, bool enable) {
+ // unify naming convention
+#if !defined(USBPHY1) && defined(USBPHY)
+ #define USBPHY1 USBPHY
+#endif
+
+ if (port == 0) {
+ if (enable) {
+ USBPHY1->CTRL_SET = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK;
+ } else {
+ USBPHY1->CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK;
+ }
+ }
+#if FSL_FEATURE_SOC_USBPHY_COUNT > 1U
+ else if (port == 1) {
+ if (enable) {
+ USBPHY2->CTRL_SET = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK;
+ } else {
+ USBPHY2->CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK;
+ }
+ }
+#endif
+
+#if !defined(USBPHY1) && defined(USBPHY)
+ #undef USBPHY1
+#endif
+}
+#endif
+
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
uint32_t hcd_frame_number(uint8_t rhport) {
(void) rhport;
- return (ehci_data.uframe_number + ehci_data.regs->frame_index) >> 3;
+ uint32_t uframe = ehci_data.regs->frame_index;
+ return (ehci_data.uframe_number + uframe) >> 3;
}
void hcd_port_reset(uint8_t rhport) {
@@ -212,16 +249,21 @@ void hcd_port_reset_end(uint8_t rhport) {
(void) rhport;
ehci_registers_t* regs = ehci_data.regs;
- // skip if reset is already complete
- if (!regs->portsc_bm.port_reset) {
- return;
- }
+ // stop reset only if is not complete yet
+ if (regs->portsc_bm.port_reset) {
+ // mask out all change bits since they are Write 1 to clear
+ uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
+ portsc &= ~EHCI_PORTSC_MASK_PORT_RESET;
- // mask out all change bits since they are Write 1 to clear
- uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
- portsc &= ~EHCI_PORTSC_MASK_PORT_RESET;
+ regs->portsc = portsc;
+ }
- regs->portsc = portsc;
+#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U))
+ // Enable disconnect detector for highspeed device only
+ if (hcd_port_speed_get(rhport) == TUSB_SPEED_HIGH) {
+ nxp_usbphy_disconn_detector_set(rhport, true);
+ }
+#endif
}
bool hcd_port_connect_status(uint8_t rhport) {
@@ -578,6 +620,10 @@ void port_connect_status_change_isr(uint8_t rhport) {
hcd_event_device_attach(rhport, true);
} else // device unplugged
{
+#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U))
+ // Disable disconnect detector
+ nxp_usbphy_disconn_detector_set(rhport, false);
+#endif
hcd_event_device_remove(rhport, true);
}
}
@@ -821,14 +867,21 @@ static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
}
ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
+
+ // protect qhd_pool since 'used' and 'removing' can be changed in isr
+ ehci_qhd_t *result = NULL;
+ usbh_spin_lock(false);
for (uint32_t i = 0; i < QHD_MAX; i++) {
if ((qhd_pool[i].dev_addr == dev_addr) &&
- ep_addr == qhd_ep_addr(&qhd_pool[i])) {
- return &qhd_pool[i];
+ ep_addr == qhd_ep_addr(&qhd_pool[i]) &&
+ qhd_pool[i].used && !qhd_pool[i].removing) {
+ result = &qhd_pool[i];
+ break;
}
}
+ usbh_spin_unlock(false);
- return NULL;
+ return result;
}
// Init queue head with endpoint descriptor
@@ -866,8 +919,8 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
if (interval < 4) {
// sub millisecond interval
p_qhd->interval_ms = 0;
- p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
- (interval == 2) ? TU_BIN8(10101010): TU_BIN8(01000100);
+ p_qhd->int_smask = (interval == 1) ? 0xff : // 0b11111111
+ (interval == 2) ? 0xaa /* 0b10101010 */ : 0x44 /* 0b01000100 */;
} else {
p_qhd->interval_ms = (uint8_t) tu_min16(1 << (interval - 4), 255);
p_qhd->int_smask = TU_BIT(interval % 8);
@@ -876,7 +929,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
TU_ASSERT(0 != interval, );
// Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes
p_qhd->int_smask = 0x01;
- p_qhd->fl_int_cmask = TU_BIN8(11100);
+ p_qhd->fl_int_cmask = 0x1c; // 0b11100
p_qhd->interval_ms = interval;
}
break;
@@ -896,7 +949,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
p_qhd->used = 1;
p_qhd->removing = 0;
p_qhd->attached_qtd = NULL;
- p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint
+ p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint
//------------- active, but no TD list -------------//
p_qhd->qtd_overlay.halted = 0;
diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h
index 87659701b..719bdeafc 100644
--- a/src/portable/ehci/ehci.h
+++ b/src/portable/ehci/ehci.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_EHCI_H_
-#define _TUSB_EHCI_H_
+#ifndef TUSB_EHCI_H_
+#define TUSB_EHCI_H_
/* Abbreviation
@@ -458,4 +458,4 @@ TU_VERIFY_STATIC(sizeof(ehci_cap_registers_t) == 16, "size is not correct");
}
#endif
-#endif /* _TUSB_EHCI_H_ */
+#endif /* TUSB_EHCI_H_ */
diff --git a/src/portable/ehci/ehci_api.h b/src/portable/ehci/ehci_api.h
index 12e0a73d7..79fbe702a 100644
--- a/src/portable/ehci/ehci_api.h
+++ b/src/portable/ehci/ehci_api.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_EHCI_API_H_
-#define _TUSB_EHCI_API_H_
+#ifndef TUSB_EHCI_API_H_
+#define TUSB_EHCI_API_H_
#ifdef __cplusplus
extern "C" {
diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c
deleted file mode 100644
index 1b6aae026..000000000
--- a/src/portable/espressif/esp32sx/dcd_esp32sx.c
+++ /dev/null
@@ -1,889 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- * Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. 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.
- */
-
-#include "tusb_option.h"
-
-#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
-
-// Espressif
-#include "xtensa/xtensa_api.h"
-
-#include "esp_intr_alloc.h"
-#include "esp_log.h"
-#include "soc/dport_reg.h"
-#include "soc/gpio_sig_map.h"
-#include "soc/usb_periph.h"
-#include "soc/usb_reg.h"
-#include "soc/usb_struct.h"
-#include "soc/periph_defs.h" // for interrupt source
-
-#include "device/dcd.h"
-
-#ifndef USB_OUT_EP_NUM
-#define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0])))
-#endif
-
-#ifndef USB_IN_EP_NUM
-#define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0])))
-#endif
-
-// Max number of bi-directional endpoints including EP0
-// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0
-// We should probably prohibit enabling Endpoint IN > 4 (not done yet)
-#define EP_MAX USB_OUT_EP_NUM
-
-// FIFO size in bytes
-#define EP_FIFO_SIZE 1024
-
-// Max number of IN EP FIFOs
-#define EP_FIFO_NUM 5
-
-typedef struct {
- uint8_t *buffer;
- // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
- uint16_t total_len;
- uint16_t queued_len;
- uint16_t max_size;
- bool short_packet;
- uint8_t interval;
-} xfer_ctl_t;
-
-static const char *TAG = "TUSB:DCD";
-static intr_handle_t usb_ih;
-
-
-static uint32_t _setup_packet[2];
-
-#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
-static xfer_ctl_t xfer_status[EP_MAX][2];
-
-// Keep count of how many FIFOs are in use
-static uint8_t _allocated_fifos = 1; //FIFO0 is always in use
-
-// Will either return an unused FIFO number, or 0 if all are used.
-static uint8_t get_free_fifo(void)
-{
- if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++;
- return 0;
-}
-
-// Setup the control endpoint 0.
-static void bus_reset(void)
-{
- for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) {
- USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
- }
-
- // clear device address
- USB0.dcfg &= ~USB_DEVADDR_M;
-
- USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
- USB0.doepmsk = USB_SETUPMSK_M | USB_XFERCOMPLMSK;
- USB0.diepmsk = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
-
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO MAX |
- // ---------------
- // | ... |
- // --------------- y + x + 16 + GRXFSIZ
- // | IN FIFO 2 |
- // --------------- x + 16 + GRXFSIZ
- // | IN FIFO 1 |
- // --------------- 16 + GRXFSIZ
- // | IN FIFO 0 |
- // --------------- GRXFSIZ
- // | OUT FIFO |
- // | ( Shared ) |
- // --------------- 0
- //
- // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits):
- // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN
- //
- // - All EP OUT shared a unique OUT FIFO which uses
- // * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets).
- // * 2 locations for OUT endpoint control words.
- // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
- // * 1 location for global NAK (not required/used here).
- // * It is recommended to allocate 2 times the largest packet size, therefore
- // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52
- USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10,
- USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo
- USB0.grxfsiz = 52;
-
- // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
- USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL);
-
- // Ready to receive SETUP packet
- USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M;
-
- USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M;
-}
-
-static void enum_done_processing(void)
-{
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then");
- // On current silicon on the Full Speed core, speed is fixed to Full Speed.
- // However, keep for debugging and in case Low Speed is ever supported.
- uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V);
-
- // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL
- if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz)
- USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes
- USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN].max_size = 64;
- } else {
- USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes
- USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
- xfer_status[0][TUSB_DIR_OUT].max_size = 8;
- xfer_status[0][TUSB_DIR_IN].max_size = 8;
- }
-}
-
-
-/*------------------------------------------------------------------*/
-/* Controller API
- *------------------------------------------------------------------*/
-bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
- ESP_LOGV(TAG, "DCD init - Start");
-
- // A. Disconnect
- ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up");
- USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect
-
- // B. Programming DCFG
- /* If USB host misbehaves during status portion of control xfer
- (non zero-length packet), send STALL back and discard. Full speed. */
- USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL
- (3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476)
-
- USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON
- USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode
- USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides
-
- // C. Setting SNAKs, then connect
- for (int n = 0; n < USB_OUT_EP_NUM; n++) {
- USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
- }
-
- // D. Interruption masking
- USB0.gintmsk = 0; //mask all
- USB0.gotgint = ~0U; //clear OTG ints
- USB0.gintsts = ~0U; //clear pending ints
- USB0.gintmsk = USB_OTGINTMSK_M |
- USB_MODEMISMSK_M |
- USB_RXFLVIMSK_M |
- USB_ERLYSUSPMSK_M |
- USB_USBSUSPMSK_M |
- USB_USBRSTMSK_M |
- USB_ENUMDONEMSK_M |
- USB_RESETDETMSK_M |
- USB_WKUPINT_M |
- USB_DISCONNINTMSK_M; // host most only
-
- dcd_connect(rhport);
- return true;
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void)rhport;
- ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr);
- USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S);
- // Response with status after 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;
-
- // set remote wakeup
- USB0.dctl |= USB_RMTWKUPSIG_M;
-
- // enable SOF to detect bus resume
- USB0.gintsts = USB_SOF_M;
- USB0.gintmsk |= USB_SOFMSK_M;
-
- // Per specs: remote wakeup signal bit must be clear within 1-15ms
- vTaskDelay(pdMS_TO_TICKS(1));
-
- USB0.dctl &= ~USB_RMTWKUPSIG_M;
-}
-
-// connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- USB0.dctl &= ~USB_SFTDISCON_M;
-}
-
-// disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- USB0.dctl |= USB_SFTDISCON_M;
-}
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- // TODO implement later
-}
-
-/*------------------------------------------------------------------*/
-/* DCD Endpoint port
- *------------------------------------------------------------------*/
-
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
-{
- ESP_LOGV(TAG, "DCD endpoint opened");
- (void)rhport;
-
- usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
- usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
-
- uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
-
- TU_ASSERT(epnum < EP_MAX);
-
- xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
- xfer->max_size = tu_edpt_packet_size(desc_edpt);
- xfer->interval = desc_edpt->bInterval;
-
- if (dir == TUSB_DIR_OUT) {
- out_ep[epnum].doepctl |= USB_USBACTEP1_M |
- desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) |
- xfer->max_size << USB_MPS1_S;
- USB0.daintmsk |= (1 << (16 + epnum));
- } else {
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO MAX |
- // ---------------
- // | ... |
- // --------------- y + x + 16 + GRXFSIZ
- // | IN FIFO 2 |
- // --------------- x + 16 + GRXFSIZ
- // | IN FIFO 1 |
- // --------------- 16 + GRXFSIZ
- // | IN FIFO 0 |
- // --------------- GRXFSIZ
- // | OUT FIFO |
- // | ( Shared ) |
- // --------------- 0
- //
- // Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints
- // - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1)
- // - Offset: GRXFSIZ + 16 + Size*(epnum-1)
- // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
-
- uint8_t fifo_num = get_free_fifo();
- TU_ASSERT(fifo_num != 0);
-
- in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M);
- in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
- fifo_num << USB_D_TXFNUM1_S |
- desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
- xfer->max_size << 0;
-
- USB0.daintmsk |= (1 << (0 + epnum));
-
- // Both TXFD and TXSA are in unit of 32-bit words.
- // IN FIFO 0 was configured during enumeration, hence the "+ 16".
- uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16;
- uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1);
- uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1);
-
- // DIEPTXF starts at FIFO #1.
- USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset;
- }
- return true;
-}
-
-void dcd_edpt_close_all(uint8_t rhport)
-{
- (void) rhport;
-
- usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
- usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
-
- // Disable non-control interrupt
- USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
-
- for(uint8_t n = 1; n < EP_MAX; n++)
- {
- // disable OUT endpoint
- out_ep[n].doepctl = 0;
- xfer_status[n][TUSB_DIR_OUT].max_size = 0;
-
- // disable IN endpoint
- in_ep[n].diepctl = 0;
- xfer_status[n][TUSB_DIR_IN].max_size = 0;
- }
-
- _allocated_fifos = 1;
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
-{
- (void)rhport;
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
- xfer->total_len = total_bytes;
- xfer->queued_len = 0;
- xfer->short_packet = false;
-
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint8_t short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if (short_packet_size > 0 || (total_bytes == 0)) {
- num_packets++;
- }
-
- ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
- epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
- num_packets, total_bytes);
-
- // IN and OUT endpoint xfers are interrupt-driven, we just schedule them
- // here.
- if (dir == TUSB_DIR_IN) {
- // A full IN transfer (multiple packets, possibly) triggers XFRC.
- USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
- USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
-
- // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
- if ((USB0.in_ep_reg[epnum].diepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
- USB0.in_ep_reg[epnum].diepctl |= (odd_frame_now ? USB_DI_SETD0PID1 : USB_DI_SETD1PID1);
- }
-
- // Enable fifo empty interrupt only if there are something to put in the fifo.
- if(total_bytes != 0) {
- USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
- }
- } else {
- // Each complete packet for OUT xfers triggers XFRC.
- USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
- USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
-
- // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
- if ((USB0.out_ep_reg[epnum].doepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
- USB0.out_ep_reg[epnum].doepctl |= (odd_frame_now ? USB_DO_SETD0PID1 : USB_DO_SETD1PID1);
- }
- }
- return true;
-}
-
-#if 0 // TODO support dcd_edpt_xfer_fifo API
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
- (void)rhport;
-}
-#endif
-
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void)rhport;
-
- usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
- usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- if (dir == TUSB_DIR_IN) {
- // Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) {
- in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M);
- } else {
- // Stop transmitting packets and NAK IN xfers.
- in_ep[epnum].diepctl |= USB_DI_SNAK1_M;
- while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ;
-
- // Disable the endpoint. Note that both SNAK and STALL are set here.
- in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M);
- while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ;
- in_ep[epnum].diepint = USB_D_EPDISBLD0_M;
- }
-
- // Flush the FIFO, and wait until we have confirmed it cleared.
- uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V);
- USB0.grstctl |= (fifo_num << USB_TXFNUM_S);
- USB0.grstctl |= USB_TXFFLSH_M;
- while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ;
- } else {
- // Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) {
- out_ep[epnum].doepctl |= USB_STALL0_M;
- } else {
- // Asserting GONAK is required to STALL an OUT endpoint.
- // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
- // anyway, and it can't be cleared by user code. If this while loop never
- // finishes, we have bigger problems than just the stack.
- USB0.dctl |= USB_SGOUTNAK_M;
- while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ;
-
- // Ditto here- disable the endpoint. Note that only STALL and not SNAK
- // is set here.
- out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M);
- while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ;
- out_ep[epnum].doepint = USB_EPDISBLD0_M;
-
- // Allow other OUT endpoints to keep receiving.
- USB0.dctl |= USB_CGOUTNAK_M;
- }
- }
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void)rhport;
-
- usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
- usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- if (dir == TUSB_DIR_IN) {
- in_ep[epnum].diepctl &= ~USB_D_STALL1_M;
-
- uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S;
- // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
- // and bulk endpoints.
- if (eptype == 2 || eptype == 3) {
- in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M;
- }
- } else {
- out_ep[epnum].doepctl &= ~USB_STALL1_M;
-
- uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S;
- // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
- // and bulk endpoints.
- if (eptype == 2 || eptype == 3) {
- out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M;
- }
- }
-}
-
-/*------------------------------------------------------------------*/
-
-static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size)
-{
- ESP_EARLY_LOGV(TAG, "USB - receive_packet");
- volatile uint32_t *rx_fifo = USB0.fifo[0];
-
- // See above TODO
- // uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos;
- // xfer->queued_len = xfer->total_len - remaining;
-
- uint16_t remaining = xfer->total_len - xfer->queued_len;
- uint16_t to_recv_size;
-
- if (remaining <= xfer->max_size) {
- // Avoid buffer overflow.
- to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
- } else {
- // Room for full packet, choose recv_size based on what the microcontroller
- // claims.
- to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
- }
-
- // Common buffer read
-#if 0 // TODO support dcd_edpt_xfer_fifo API
- if (xfer->ff)
- {
- // Ring buffer
- tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size);
- }
- else
-#endif
- {
- uint8_t to_recv_rem = to_recv_size % 4;
- uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
-
- // Do not assume xfer buffer is aligned.
- uint8_t *base = (xfer->buffer + xfer->queued_len);
-
- // This for loop always runs at least once- skip if less than 4 bytes
- // to collect.
- if (to_recv_size >= 4) {
- for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
- uint32_t tmp = (*rx_fifo);
- base[i] = tmp & 0x000000FF;
- base[i + 1] = (tmp & 0x0000FF00) >> 8;
- base[i + 2] = (tmp & 0x00FF0000) >> 16;
- base[i + 3] = (tmp & 0xFF000000) >> 24;
- }
- }
-
- // Do not read invalid bytes from RX FIFO.
- if (to_recv_rem != 0) {
- uint32_t tmp = (*rx_fifo);
- uint8_t *last_32b_bound = base + to_recv_size_aligned;
-
- last_32b_bound[0] = tmp & 0x000000FF;
- if (to_recv_rem > 1) {
- last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
- }
- if (to_recv_rem > 2) {
- last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
- }
- }
- }
-
- xfer->queued_len += xfer_size;
-
- // Per USB spec, a short OUT packet (including length 0) is always
- // indicative of the end of a transfer (at least for ctl, bulk, int).
- xfer->short_packet = (xfer_size < xfer->max_size);
-}
-
-static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num)
-{
- ESP_EARLY_LOGV(TAG, "USB - transmit_packet");
- volatile uint32_t *tx_fifo = USB0.fifo[fifo_num];
-
- uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S;
- xfer->queued_len = xfer->total_len - remaining;
-
- uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
-
-#if 0 // TODO support dcd_edpt_xfer_fifo API
- if (xfer->ff)
- {
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size);
- }
- else
-#endif
- {
- uint8_t to_xfer_rem = to_xfer_size % 4;
- uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
-
- // Buffer might not be aligned to 32b, so we need to force alignment
- // by copying to a temp var.
- uint8_t *base = (xfer->buffer + xfer->queued_len);
-
- // This for loop always runs at least once- skip if less than 4 bytes
- // to send off.
- if (to_xfer_size >= 4) {
- for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
- uint32_t tmp = base[i] | (base[i + 1] << 8) |
- (base[i + 2] << 16) | (base[i + 3] << 24);
- (*tx_fifo) = tmp;
- }
- }
-
- // Do not read beyond end of buffer if not divisible by 4.
- if (to_xfer_rem != 0) {
- uint32_t tmp = 0;
- uint8_t *last_32b_bound = base + to_xfer_size_aligned;
-
- tmp |= last_32b_bound[0];
- if (to_xfer_rem > 1) {
- tmp |= (last_32b_bound[1] << 8);
- }
- if (to_xfer_rem > 2) {
- tmp |= (last_32b_bound[2] << 16);
- }
-
- (*tx_fifo) = tmp;
- }
- }
-}
-
-static void read_rx_fifo(void)
-{
- // Pop control word off FIFO (completed xfers will have 2 control words,
- // we only pop one ctl word each interrupt).
- uint32_t const ctl_word = USB0.grxstsp;
- uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S;
- uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S;
- uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S;
-
- switch (pktsts) {
- case 0x01: // Global OUT NAK (Interrupt)
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK");
- break;
-
- case 0x02: { // Out packet recvd
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet");
- xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- receive_packet(xfer, bcnt);
- }
- break;
-
- case 0x03: // Out packet done (Interrupt)
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done");
- break;
-
- case 0x04: // Step 2: Setup transaction completed (Interrupt)
- // After this event, OEPINT interrupt will occur with SETUP bit set
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done");
- USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M;
- break;
-
- case 0x06: { // Step1: Setup data packet received
- volatile uint32_t *rx_fifo = USB0.fifo[0];
-
- // We can receive up to three setup packets in succession, but
- // only the last one is valid. Therefore we just overwrite it
- _setup_packet[0] = (*rx_fifo);
- _setup_packet[1] = (*rx_fifo);
-
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]);
- }
- break;
-
- default: // Invalid, do something here, like breakpoint?
- TU_BREAKPOINT();
- break;
- }
-}
-
-static void handle_epout_ints(void)
-{
- // GINTSTS will be cleared with DAINT == 0
- // DAINT for a given EP clears when DOEPINTx is cleared.
- // DOEPINT will be cleared when DAINT's out bits are cleared.
- for (int n = 0; n < USB_OUT_EP_NUM; n++) {
- xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
-
- if (USB0.daint & (1 << (16 + n))) {
- // SETUP packet Setup Phase done.
- if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) {
- USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear
- dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
- }
-
- // OUT XFER complete (single packet).q
- if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) {
-
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)");
- USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M;
-
- // Transfer complete if short packet or total len is transferred
- if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
- xfer->short_packet = false;
- dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
- } else {
- // Schedule another packet to be received.
- USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
- USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
- }
- }
- }
- }
-}
-
-static void handle_epin_ints(void)
-{
- // GINTSTS will be cleared with DAINT == 0
- // DAINT for a given EP clears when DIEPINTx is cleared.
- // IEPINT will be cleared when DAINT's out bits are cleared.
- for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) {
- xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
-
- if (USB0.daint & (1 << (0 + n))) {
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n);
- // IN XFER complete (entire xfer).
- if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!");
- USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M;
- dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
-
- // XFER FIFO empty
- if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) {
- ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!");
- USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M;
- transmit_packet(xfer, &USB0.in_ep_reg[n], n);
-
- // Turn off TXFE if all bytes are written.
- if (xfer->queued_len == xfer->total_len)
- {
- USB0.dtknqr4_fifoemptymsk &= ~(1 << n);
- }
- }
-
- // XFER Timeout
- if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
- // Clear interrupt or endpoint will hang.
- USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
- // Maybe retry?
- }
- }
- }
-}
-
-
-static void _dcd_int_handler(void* arg)
-{
- (void) arg;
- uint8_t const rhport = 0;
-
- const uint32_t int_msk = USB0.gintmsk;
- const uint32_t int_status = USB0.gintsts & int_msk;
-
- if (int_status & USB_USBRST_M) {
- // start of reset
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
- USB0.gintsts = USB_USBRST_M;
- // FIFOs will be reassigned when the endpoints are reopen
- _allocated_fifos = 1;
- bus_reset();
- }
-
- if (int_status & USB_RESETDET_M) {
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend");
- USB0.gintsts = USB_RESETDET_M;
- bus_reset();
- }
-
- if (int_status & USB_ENUMDONE_M) {
- // ENUMDNE detects speed of the link. For full-speed, we
- // always expect the same value. This interrupt is considered
- // the end of reset.
- USB0.gintsts = USB_ENUMDONE_M;
- enum_done_processing();
- dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
- }
-
- if(int_status & USB_USBSUSP_M)
- {
- USB0.gintsts = USB_USBSUSP_M;
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
- }
-
- if(int_status & USB_WKUPINT_M)
- {
- USB0.gintsts = USB_WKUPINT_M;
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
- }
-
- if (int_status & USB_OTGINT_M)
- {
- // OTG INT bit is read-only
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected");
-
- uint32_t const otg_int = USB0.gotgint;
-
- if (otg_int & USB_SESENDDET_M)
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
- }
-
- USB0.gotgint = otg_int;
- }
-
- if (int_status & USB_SOF_M) {
- USB0.gintsts = USB_SOF_M;
-
- // Disable SOF interrupt since currently only used for remote wakeup detection
- USB0.gintmsk &= ~USB_SOFMSK_M;
-
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
- }
-
-
- if (int_status & USB_RXFLVI_M) {
- // RXFLVL bit is read-only
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!");
-
- // Mask out RXFLVL while reading data from FIFO
- USB0.gintmsk &= ~USB_RXFLVIMSK_M;
- read_rx_fifo();
- USB0.gintmsk |= USB_RXFLVIMSK_M;
- }
-
- // OUT endpoint interrupt handling.
- if (int_status & USB_OEPINT_M) {
- // OEPINT is read-only
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!");
- handle_epout_ints();
- }
-
- // IN endpoint interrupt handling.
- if (int_status & USB_IEPINT_M) {
- // IEPINT bit read-only
- ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!");
- handle_epin_ints();
- }
-
- // Without handling
- USB0.gintsts |= USB_CURMOD_INT_M |
- USB_MODEMIS_M |
- USB_OTGINT_M |
- USB_NPTXFEMP_M |
- USB_GINNAKEFF_M |
- USB_GOUTNAKEFF |
- USB_ERLYSUSP_M |
- USB_USBSUSP_M |
- USB_ISOOUTDROP_M |
- USB_EOPF_M |
- USB_EPMIS_M |
- USB_INCOMPISOIN_M |
- USB_INCOMPIP_M |
- USB_FETSUSP_M |
- USB_PTXFEMP_M;
-}
-
-void dcd_int_enable (uint8_t rhport)
-{
- (void) rhport;
- esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) _dcd_int_handler, NULL, &usb_ih);
-}
-
-void dcd_int_disable (uint8_t rhport)
-{
- (void) rhport;
- esp_intr_free(usb_ih);
-}
-
-#endif // #if OPT_MCU_ESP32S2 || OPT_MCU_ESP32S3
diff --git a/src/portable/mentor/musb/musb_max32.h b/src/portable/mentor/musb/musb_max32.h
index 35849b5f8..599de2ca1 100644
--- a/src/portable/mentor/musb/musb_max32.h
+++ b/src/portable/mentor/musb/musb_max32.h
@@ -31,7 +31,17 @@
extern "C" {
#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
#include "mxc_device.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
#include "usbhs_regs.h"
#define MUSB_CFG_SHARED_FIFO 1 // shared FIFO for TX and RX endpoints
diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c
index b4a698199..8413a7ddd 100644
--- a/src/portable/microchip/pic/dcd_pic.c
+++ b/src/portable/microchip/pic/dcd_pic.c
@@ -687,6 +687,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
if (ie) intr_enable(rhport);
}
+#if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false;
+}
+#endif
+
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
@@ -866,5 +881,4 @@ void dcd_int_handler(uint8_t rhport)
intr_clear(rhport);
}
-
#endif
diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c
index 8709baf67..b039bb505 100644
--- a/src/portable/microchip/pic32mz/dcd_pic32mz.c
+++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c
@@ -162,13 +162,7 @@ void dcd_remote_wakeup(uint8_t rhport)
(void) rhport;
USB_REGS->POWERbits.RESUME = 1;
-#if CFG_TUSB_OS != OPT_OS_NONE
- osal_task_delay(10);
-#else
- // TODO: Wait in non blocking mode
- unsigned cnt = 2000;
- while (cnt--) __asm__("nop");
-#endif
+ tusb_time_delay_ms_api(10);
USB_REGS->POWERbits.RESUME = 0;
}
@@ -444,12 +438,20 @@ void dcd_edpt_close_all (uint8_t rhport)
}
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void) rhport;
(void) ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
+
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);
@@ -750,5 +752,4 @@ void dcd_int_handler(uint8_t rhport)
}
}
}
-
#endif
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 357aa1549..383cf60e5 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -26,10 +26,7 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
- CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \
- CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21)
+#if CFG_TUD_ENABLED && TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X, OPT_MCU_SAMD51, OPT_MCU_SAME5X)
#include "sam.h"
#include "device/dcd.h"
@@ -106,10 +103,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
-#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
-
-void dcd_int_enable(uint8_t rhport)
-{
+#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X)
+void dcd_int_enable(uint8_t rhport) {
(void) rhport;
NVIC_EnableIRQ(USB_0_IRQn);
NVIC_EnableIRQ(USB_1_IRQn);
@@ -117,8 +112,7 @@ void dcd_int_enable(uint8_t rhport)
NVIC_EnableIRQ(USB_3_IRQn);
}
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
(void) rhport;
NVIC_DisableIRQ(USB_3_IRQn);
NVIC_DisableIRQ(USB_2_IRQn);
@@ -126,17 +120,13 @@ void dcd_int_disable(uint8_t rhport)
NVIC_DisableIRQ(USB_0_IRQn);
}
-#elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
- CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21
-
-void dcd_int_enable(uint8_t rhport)
-{
+#elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X)
+void dcd_int_enable(uint8_t rhport) {
(void) rhport;
NVIC_EnableIRQ(USB_IRQn);
}
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
(void) rhport;
NVIC_DisableIRQ(USB_IRQn);
}
@@ -255,11 +245,17 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) {
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void) rhport;
(void) ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
- // TODO: implement if necessary?
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
void dcd_edpt_close_all (uint8_t rhport)
@@ -437,5 +433,4 @@ void dcd_int_handler (uint8_t rhport)
// Handle complete transfer
maybe_transfer_complete();
}
-
#endif
diff --git a/src/portable/microchip/samd/hcd_samd.c b/src/portable/microchip/samd/hcd_samd.c
new file mode 100644
index 000000000..0de7ddeb6
--- /dev/null
+++ b/src/portable/microchip/samd/hcd_samd.c
@@ -0,0 +1,761 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 ChrisDeadman
+ *
+ * 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 && !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) && \
+ TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X, OPT_MCU_SAMD51, OPT_MCU_SAME5X)
+
+#include "host/hcd.h"
+#include "sam.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define USB_HOST_PTYPE_DIS 0x0
+#define USB_HOST_PTYPE_CTRL 0x1
+#define USB_HOST_PTYPE_ISO 0x2
+#define USB_HOST_PTYPE_BULK 0x3
+#define USB_HOST_PTYPE_INT 0x4
+#define USB_HOST_PTYPE_EXT 0x5
+
+#define USB_HOST_PCFG_PTOKEN_SETUP 0x0
+#define USB_HOST_PCFG_PTOKEN_IN 0x1
+#define USB_HOST_PCFG_PTOKEN_OUT 0x2
+
+#define USB_PCKSIZE_ENUM(size) \
+ ((size) >= 1024 ? 7 \
+ : (size) >= 1023 ? 7 \
+ : (size) > 256 ? 6 \
+ : (size) > 128 ? 5 \
+ : (size) > 64 ? 4 \
+ : (size) > 32 ? 3 \
+ : (size) > 16 ? 2 \
+ : (size) > 8 ? 1 \
+ : 0)
+
+// Uncomment to use fake frame number.
+// Low-Speed devices stall FNUM during enumeration :/
+// #define HCD_SAMD_FAKE_FNUM
+
+typedef struct {
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint16_t max_packet_size;
+ uint16_t xfer_length;
+ uint16_t xfer_remaining;
+} usb_pipe_status_t;
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile UsbHostDescriptor usb_pipe_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile usb_pipe_status_t usb_pipe_status_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile uint32_t fake_fnum;
+
+static uint8_t samd_configure_pipe(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t pipe;
+ uint8_t token;
+ volatile usb_pipe_status_t* pipe_status;
+ bool same_addr = false;
+ bool same_ep_addr = false;
+
+ // evaluate pipe token
+ token = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_HOST_PCFG_PTOKEN_IN
+ : tu_edpt_number(ep_addr) == 0 ? USB_HOST_PCFG_PTOKEN_SETUP
+ : USB_HOST_PCFG_PTOKEN_OUT;
+
+ TU_LOG3("samd_configure_pipe(token=%02X, dev_addr=%02X, ep_addr=%02X)=", token, dev_addr, ep_addr);
+
+ // find already allocated pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ same_addr = (pipe_status->dev_addr == dev_addr);
+ same_ep_addr = (tu_edpt_number(pipe_status->ep_addr) == tu_edpt_number(ep_addr));
+ if (same_ep_addr && (same_addr || (tu_edpt_number(ep_addr) == 0))) {
+ break;
+ }
+ }
+
+ // allocate from pool of free pipes
+ if (pipe >= USB_PIPE_NUM) {
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ // found a free pipe
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ break;
+ }
+ }
+ }
+
+ // no pipe available :(
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return pipe;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer should be in progress
+ TU_ASSERT(((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_DIS) ||
+ USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1),
+ USB_PIPE_NUM);
+
+ // update addr and ep_addr
+ pipe_status->dev_addr = dev_addr;
+ pipe_status->ep_addr = ep_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PDADDR = dev_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PEPNUM = tu_edpt_number(ep_addr);
+
+ // token specific configuration
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTOKEN = token;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ if (token == USB_HOST_PCFG_PTOKEN_SETUP) {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TXSTP;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else if (token == USB_HOST_PCFG_PTOKEN_IN) {
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ }
+
+ return pipe;
+}
+
+static void samd_free_pipe(uint8_t pipe)
+{
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->dev_addr = UINT8_MAX;
+ pipe_status->ep_addr = UINT8_MAX;
+ pipe_status->max_packet_size = 0;
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ memset((uint8_t*)(uintptr_t) &usb_pipe_table[pipe], 0, sizeof(usb_pipe_table[pipe]));
+}
+
+static void samd_free_all_pipes(void)
+{
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ samd_free_pipe(pipe);
+ }
+}
+
+static bool samd_on_xfer(uint8_t pipe, xfer_result_t xfer_result)
+{
+ uint16_t xfer_delta;
+ bool xfer_complete;
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+
+ // freeze the pipe
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+
+ // get number of transferred bytes
+ if (xfer_result == XFER_RESULT_SUCCESS) {
+ xfer_delta = usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
+ } else {
+ xfer_delta = 0;
+ }
+
+ TU_LOG3("samd_on_xfer(%d, result=%d, xdelta=%d, rem=%d)\r\n", xfer_result, pipe, xfer_delta, pipe_status->xfer_remaining);
+
+ // update pipe status
+ if (xfer_delta > pipe_status->xfer_remaining) {
+ xfer_delta = pipe_status->xfer_remaining;
+ }
+ pipe_status->xfer_remaining -= xfer_delta;
+ pipe_status->xfer_length += xfer_delta;
+
+ // last packet handling
+ if (xfer_delta < pipe_status->max_packet_size) {
+ pipe_status->xfer_remaining = 0;
+ }
+
+ // transfer complete
+ xfer_complete = (xfer_result != XFER_RESULT_SUCCESS) || (pipe_status->xfer_remaining == 0);
+ if (xfer_complete) {
+ return true;
+ }
+
+ // continue receiving
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // continue sending
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // advance packet buffer
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg += xfer_delta;
+
+ // start next transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// Interrupt Handler
+void hcd_int_handler(uint8_t rhport, bool in_isr)
+{
+ (void) rhport;
+
+ uint16_t int_flags;
+ uint8_t pint_flags;
+ xfer_result_t xfer_result;
+ volatile usb_pipe_status_t* pipe_status;
+
+ //
+ // Check INTFLAG
+ //
+ int_flags = USB->HOST.INTFLAG.reg;
+ if (int_flags & USB_HOST_INTFLAG_HSOF) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_HSOF;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RST) {
+ TU_LOG2("USB_HOST_INTFLAG_RST\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ }
+ if (int_flags & USB_HOST_INTFLAG_WAKEUP) {
+ TU_LOG3("USB_HOST_INTFLAG_WAKEUP\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_WAKEUP;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DNRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_DNRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DNRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_UPRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_UPRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_UPRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RAMACER) {
+ TU_LOG1("USB_HOST_INTFLAG_RAMACER\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RAMACER;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DCONN) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DCONN;
+ hcd_event_device_attach(rhport, in_isr);
+ }
+ if (int_flags & USB_HOST_INTFLAG_DDISC) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DDISC;
+ hcd_event_device_remove(rhport, in_isr);
+ }
+
+ // handle pipe interrupts
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ // get pipe handle
+ pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ continue;
+ }
+
+ //
+ // Check PINTFLAG
+ //
+ pint_flags = USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+ xfer_result = XFER_RESULT_INVALID;
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT0) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT0;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT1) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT1;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TXSTP) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TXSTP;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_STALL) {
+ TU_LOG2("USB_HOST_PINTFLAG_STALL\r\n");
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_STALL;
+ xfer_result = XFER_RESULT_STALLED;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRFAIL) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRFAIL;
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_ERRORFLOW) {
+ TU_LOG1("USB_HOST_STATUS_BK_ERRORFLOW\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_CRCERR) {
+ TU_LOG1("USB_HOST_STATUS_BK_CRCERR\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else {
+ // SAMD Quirk #1:
+ // Likes to report TRFAIL for no apparent reason -> ignore
+ }
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_PERR) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_PERR;
+ // Handled by STATUS_PIPE checks below
+ }
+
+ //
+ // Check STATUS_PIPE
+ //
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DTGLER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DTGLER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DTGLER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DAPIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DAPIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DAPIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_PIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_PIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_PIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_CRC16ER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_CRC16ER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_CRC16ER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_TOUTER) {
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_TOUTER;
+
+ if ((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_INT) &&
+ (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN)) {
+ // ignore timeouts from INT pipes
+ } else {
+ if (xfer_result == XFER_RESULT_INVALID) {
+ xfer_result = XFER_RESULT_TIMEOUT;
+ }
+ }
+ }
+
+ // prevent PERR from too high error counts, that is handled by TinyUSB anyways
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.bit.ERCNT = 0;
+
+ // no updates
+ if (xfer_result == XFER_RESULT_INVALID) {
+ continue;
+ }
+
+ // continue / complete transfer
+ if (samd_on_xfer(pipe, xfer_result)) {
+ hcd_event_xfer_complete(pipe_status->dev_addr, pipe_status->ep_addr, pipe_status->xfer_length, xfer_result, true);
+ }
+ }
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ TU_ASSERT(rhport == 0);
+ (void) rh_init;
+ fake_fnum = 0;
+
+ // reset to get in a clean state.
+ USB->HOST.CTRLA.bit.SWRST = 1;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 0)
+ ;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 1)
+ ;
+
+ // load pad calibration
+ USB->HOST.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+ USB->HOST.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+ USB->HOST.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+ USB->HOST.QOSCTRL.bit.CQOS = 3; // High Quality
+ USB->HOST.QOSCTRL.bit.DQOS = 3; // High Quality
+
+ // configure host-mode
+ samd_free_all_pipes(); // initializes pipe handles and usb_pipe_table
+ USB->HOST.DESCADD.reg = (uint32_t) (&usb_pipe_table[0]);
+ USB->HOST.CTRLB.reg = USB_HOST_CTRLB_SPDCONF_NORMAL | USB_HOST_CTRLB_VBUSOK;
+ USB->HOST.CTRLA.reg = USB_CTRLA_MODE_HOST | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
+ while (USB->HOST.SYNCBUSY.bit.ENABLE == 1)
+ ;
+
+ // enable basic USB interrupts
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.INTENCLR.reg = USB_HOST_INTENCLR_MASK;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DCONN;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DDISC;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_WAKEUP;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_RST;
+
+ return true;
+}
+
+#if TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X)
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_0_IRQn);
+ NVIC_EnableIRQ(USB_1_IRQn);
+ NVIC_EnableIRQ(USB_2_IRQn);
+ NVIC_EnableIRQ(USB_3_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_3_IRQn);
+ NVIC_DisableIRQ(USB_2_IRQn);
+ NVIC_DisableIRQ(USB_1_IRQn);
+ NVIC_DisableIRQ(USB_0_IRQn);
+}
+
+#elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAMD21, OPT_MCU_SAML2X)
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+#else
+
+#error "No implementation available for hcd_int_enable / hcd_int_disable"
+
+#endif
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport)
+{
+ (void) rhport;
+
+// SAMD Quirk #2:
+// FNUM is stalled before enumeration of Low-Speed devices.
+// internal frame counter can be used as workaround (not very accurate)
+#ifdef HCD_SAMD_FAKE_FNUM
+ uint8_t start, current, prev;
+ uint8_t loop_count = (USB->HOST.STATUS.bit.SPEED == TUSB_SPEED_HIGH) ? 8 : 1;
+ for (uint8_t i = 0; i < loop_count; i++) {
+ start = USB->HOST.FLENHIGH.reg;
+ current = start;
+ // wait until wrap-around
+ prev = current;
+ while (current <= start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ // wait until start is reached again
+ prev = current;
+ while (current > start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ }
+ fake_fnum += 1;
+ return fake_fnum;
+#else
+ return USB->HOST.FNUM.bit.FNUM;
+#endif // HCD_SAMD_FAKE_FNUM
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport)
+{
+ TU_ASSERT(rhport == 0);
+ return USB->HOST.STATUS.bit.LINESTATE != 0;
+}
+
+// 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)
+{
+ hcd_int_disable(rhport);
+ samd_free_all_pipes();
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.CTRLB.bit.BUSRESET = 1;
+ fake_fnum = 0;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport)
+{
+ while (USB->HOST.INTFLAG.bit.RST == 0)
+ ;
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ USB->HOST.CTRLB.bit.SOFE = 1;
+ hcd_int_enable(rhport);
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+ (void) rhport;
+
+ switch (USB->HOST.STATUS.bit.SPEED) {
+ case 0:
+ return TUSB_SPEED_FULL;
+ case 1:
+ return TUSB_SPEED_LOW;
+ case 2:
+ return TUSB_SPEED_HIGH;
+ default:
+ return TUSB_SPEED_INVALID;
+ }
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr == dev_addr) {
+ samd_free_pipe(pipe);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+ const uint8_t ep_addr = ep_desc->bEndpointAddress;
+ const uint8_t bmAttributes = (ep_desc->bmAttributes.xfer) |
+ ((ep_desc->bmAttributes.sync) << 2) |
+ ((ep_desc->bmAttributes.usage) << 4);
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // initial configuration
+ pipe_status = &usb_pipe_status_table[pipe];
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE = bmAttributes + 1;
+ USB->HOST.HostPipe[pipe].BINTERVAL.reg = ep_desc->bInterval;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ pipe_status->max_packet_size = ep_desc->wMaxPacketSize;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.SIZE = USB_PCKSIZE_ENUM(pipe_status->max_packet_size);
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.AUTO_ZLP = 0;
+
+ return true;
+}
+
+// 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 dev_addr, uint8_t const setup_packet[8])
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, 0);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) setup_packet;
+ pipe_status->xfer_remaining = 8;
+ pipe_status->xfer_length = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 8;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t* buffer, uint16_t buflen)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) buffer;
+ pipe_status->xfer_remaining = buflen;
+ pipe_status->xfer_length = 0;
+ // receive data
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // send data
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// 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)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_abort_xfer(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer in progress
+ if (USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1) {
+ return false;
+ }
+
+ // abort the transfer
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ return true;
+}
+
+// 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)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_clear_stall(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // clear stalled state
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+
+ return true;
+}
+
+#endif
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index a5c768839..879b2eb2b 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -270,9 +270,17 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport; (void) ep_addr;
- // TODO implement dcd_edpt_close()
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
void dcd_edpt_close_all (uint8_t rhport)
@@ -494,5 +502,4 @@ void dcd_int_handler(uint8_t rhport)
}
}
}
-
#endif
diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c
index 8aec1568d..51308f7a2 100644
--- a/src/portable/microchip/samx7x/dcd_samx7x.c
+++ b/src/portable/microchip/samx7x/dcd_samx7x.c
@@ -559,15 +559,17 @@ void dcd_edpt_close_all (uint8_t rhport)
// TODO implement dcd_edpt_close_all()
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void) rhport;
- uint8_t const epnum = tu_edpt_number(ep_addr);
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false;
+}
- // Disable endpoint interrupt
- USB_REG->DEVIDR = 1 << (DEVIDR_PEP_0_Pos + epnum);
- // Disable EP
- USB_REG->DEVEPT &=~(1 << (DEVEPT_EPEN0_Pos + epnum));
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false;
}
static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix)
@@ -773,5 +775,4 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
USB_REG->DEVEPTIDR[epnum] = DEVEPTIDR_CTRL_STALLRQC;
USB_REG->DEVEPTIER[epnum] = HSTPIPIER_RSTDTS;
}
-
#endif
diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
index 1ce3da27e..a232810a2 100644
--- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -26,7 +26,7 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MM32F327X )
+#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_MM32F327X)
#include "reg_usb_otg_fs.h"
#include "mm32_device.h"
@@ -43,56 +43,53 @@ enum {
TOK_PID_SETUP = 0xDu,
};
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
union {
uint32_t head;
struct {
union {
struct {
- uint16_t : 2;
- uint16_t tok_pid : 4;
- uint16_t data : 1;
- uint16_t own : 1;
- uint16_t : 8;
+ uint16_t : 2;
+ uint16_t tok_pid : 4;
+ uint16_t data : 1;
+ 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;
+ uint16_t : 2;
+ uint16_t bdt_stall : 1;
+ uint16_t dts : 1;
+ uint16_t ninc : 1;
+ uint16_t keep : 1;
+ uint16_t : 10;
};
};
- uint16_t bc : 10;
- uint16_t : 6;
+ uint16_t bc : 10;
+ uint16_t : 6;
};
};
uint8_t *addr;
-}buffer_descriptor_t;
+} buffer_descriptor_t;
-TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
+TU_VERIFY_STATIC(sizeof(buffer_descriptor_t) == 8, "size is not correct");
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
union {
uint32_t state;
struct {
- uint32_t max_packet_size :11;
+ uint32_t max_packet_size : 11;
uint32_t : 5;
uint32_t odd : 1;
- uint32_t :15;
+ uint32_t : 15;
};
};
uint16_t length;
uint16_t remaining;
-}endpoint_state_t;
+} endpoint_state_t;
-TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
+TU_VERIFY_STATIC(sizeof(endpoint_state_t) == 8, "size is not correct");
-typedef struct
-{
+typedef struct {
union {
/* [#EP][OUT,IN][EVEN,ODD] */
buffer_descriptor_t bdt[16][2][2];
@@ -104,7 +101,7 @@ typedef struct
};
uint8_t setup_packet[8];
uint8_t addr;
-}dcd_data_t;
+} dcd_data_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -112,10 +109,9 @@ typedef struct
// 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" );
+TU_VERIFY_STATIC(sizeof(_dcd.bdt) == 512, "size is not correct");
-static void prepare_next_setup_packet(uint8_t rhport)
-{
+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;
if (_dcd.bdt[0][0][out_odd].own) {
@@ -126,12 +122,10 @@ static void prepare_next_setup_packet(uint8_t rhport)
_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));
+ 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)
-{
+static void process_stall(uint8_t rhport) {
if (USB_OTG_FS->EP_CTL[0] & USB_ENDPT_EPSTALL_MASK) {
/* clear stall condition of the control pipe */
prepare_next_setup_packet(rhport);
@@ -139,13 +133,12 @@ static void process_stall(uint8_t rhport)
}
}
-static void process_tokdne(uint8_t rhport)
-{
- const unsigned s = USB_OTG_FS->STAT;
- USB_OTG_FS->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
- buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
- endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
- unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
+static void process_tokdne(uint8_t rhport) {
+ const unsigned s = USB_OTG_FS->STAT;
+ USB_OTG_FS->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
+ buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
+ endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
+ unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
/* fetch pid before discarded by the next steps */
const unsigned pid = bd->tok_pid;
@@ -155,7 +148,7 @@ static void process_tokdne(uint8_t rhport)
bd->ninc = 0;
bd->keep = 0;
/* update the odd variable to prepare for the next transfer */
- ep->odd = odd ^ 1;
+ ep->odd = odd ^ 1;
if (pid == TOK_PID_SETUP) {
dcd_event_setup_received(rhport, bd->addr, true);
USB_OTG_FS->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
@@ -165,25 +158,24 @@ static void process_tokdne(uint8_t rhport)
TU_LOG1("TKDNE %x\r\n", s);
}
- const unsigned bc = bd->bc;
+ 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;
+ 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;
+ 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 */
+ bd->own = 1; /* the own bit must set after addr */
}
return;
}
const unsigned length = ep->length;
- dcd_event_xfer_complete(rhport,
- ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT),
- length - remaining, XFER_RESULT_SUCCESS, true);
+ dcd_event_xfer_complete(rhport, ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT), length - remaining,
+ XFER_RESULT_SUCCESS, true);
if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) {
/* After completion a ZLP of control transfer,
* it prepares for the next steup transfer. */
@@ -191,24 +183,23 @@ static void process_tokdne(uint8_t rhport)
/* When the transfer was the SetAddress,
* the device address should be updated here. */
USB_OTG_FS->ADDR = _dcd.addr;
- _dcd.addr = 0;
+ _dcd.addr = 0;
}
prepare_next_setup_packet(rhport);
}
}
-static void process_bus_reset(uint8_t rhport)
-{
- USB_OTG_FS->CTL |= USB_CTL_ODDRST_MASK;
- USB_OTG_FS->ADDR = 0;
- USB_OTG_FS->INT_ENB = (USB_OTG_FS->INT_ENB & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+static void process_bus_reset(uint8_t rhport) {
+ USB_OTG_FS->CTL |= USB_CTL_ODDRST_MASK;
+ USB_OTG_FS->ADDR = 0;
+ USB_OTG_FS->INT_ENB = (USB_OTG_FS->INT_ENB & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
USB_OTG_FS->EP_CTL[0] = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
for (unsigned i = 1; i < 16; ++i) {
USB_OTG_FS->EP_CTL[i] = 0;
}
buffer_descriptor_t *bd = _dcd.bdt[0][0];
- for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
+ for (unsigned i = 0; i < sizeof(_dcd.bdt) / sizeof(*bd); ++i, ++bd) {
bd->head = 0;
}
const endpoint_state_t ep0 = {
@@ -226,31 +217,29 @@ static void process_bus_reset(uint8_t rhport)
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
-static void process_bus_inactive(uint8_t rhport)
-{
- (void) rhport;
+static void process_bus_inactive(uint8_t rhport) {
+ (void)rhport;
const unsigned inten = USB_OTG_FS->INT_ENB;
- USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
+ USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
-static void process_bus_active(uint8_t rhport)
-{
- (void) rhport;
+static void process_bus_active(uint8_t rhport) {
+ (void)rhport;
const unsigned inten = USB_OTG_FS->INT_ENB;
- USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+ USB_OTG_FS->INT_ENB = (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;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void)rhport;
+ (void)rh_init;
tu_memclr(&_dcd, sizeof(_dcd));
- USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
+ USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
USB_OTG_FS->BDT_PAGE_02 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
USB_OTG_FS->BDT_PAGE_03 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
@@ -259,27 +248,24 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
return true;
}
#define USB_DEVICE_INTERRUPT_PRIORITY (3U)
-void dcd_int_enable(uint8_t rhport)
-{
+void dcd_int_enable(uint8_t rhport) {
uint8_t irqNumber;
irqNumber = USB_FS_IRQn;
- (void) rhport;
- USB_OTG_FS->INT_ENB = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK |
- USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
+ (void)rhport;
+ USB_OTG_FS->INT_ENB = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK |
+ USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
NVIC_SetPriority((IRQn_Type)irqNumber, USB_DEVICE_INTERRUPT_PRIORITY);
NVIC_EnableIRQ(USB_FS_IRQn);
}
-void dcd_int_disable(uint8_t rhport)
-{
- (void) rhport;
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
NVIC_DisableIRQ(USB_FS_IRQn);
USB_OTG_FS->INT_ENB = 0;
}
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void) rhport;
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void)rhport;
_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);
@@ -296,31 +282,29 @@ extern u32 SystemCoreClock;
#pragma GCC diagnostic pop
#endif
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void) rhport;
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void)rhport;
unsigned cnt = SystemCoreClock / 100;
USB_OTG_FS->CTL |= USB_CTL_RESUME_MASK;
- while (cnt--) __NOP();
+ while (cnt--) {
+ __NOP();
+ }
USB_OTG_FS->CTL &= ~USB_CTL_RESUME_MASK;
}
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- USB_OTG_FS->CTL |= USB_CTL_USBENSOFEN_MASK;
+void dcd_connect(uint8_t rhport) {
+ (void)rhport;
+ USB_OTG_FS->CTL |= USB_CTL_USBENSOFEN_MASK;
}
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- USB_OTG_FS->CTL = 0;
+void dcd_disconnect(uint8_t rhport) {
+ (void)rhport;
+ USB_OTG_FS->CTL = 0;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ (void)rhport;
+ (void)en;
// TODO implement later
}
@@ -328,24 +312,23 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
- (void) rhport;
+bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ (void)rhport;
- const unsigned ep_addr = ep_desc->bEndpointAddress;
- const unsigned epn = ep_addr & 0xFu;
- const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
- 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][0];
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ 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][0];
/* 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;
+ 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;
USB_OTG_FS->EP_CTL[epn] |= val;
@@ -359,21 +342,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
- (void) rhport;
+void dcd_edpt_close_all(uint8_t rhport) {
+ (void)rhport;
// TODO implement dcd_edpt_close_all()
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void)rhport;
- const unsigned epn = ep_addr & 0xFu;
- const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
- endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
- buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
- const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
+ const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
USB_OTG_FS->EP_CTL[epn] &= ~msk;
ep->max_packet_size = 0;
ep->length = 0;
@@ -381,14 +362,28 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
bd->head = 0;
}
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
-{
- (void) rhport;
+ #if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+ #endif
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
+ (void)rhport;
NVIC_DisableIRQ(USB_FS_IRQn);
- const unsigned epn = ep_addr & 0xFu;
- const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
- endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
- buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
if (bd->own) {
TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head);
@@ -399,42 +394,40 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
const unsigned mps = ep->max_packet_size;
if (total_bytes > mps) {
- buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
+ 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->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;
+ bd->bc = total_bytes >= mps ? mps : total_bytes;
+ bd->addr = buffer;
__DSB();
- bd->own = 1; /* the own bit must set after addr */
+ bd->own = 1; /* the own bit must set after addr */
NVIC_EnableIRQ(USB_FS_IRQn);
return true;
}
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void)rhport;
const unsigned epn = ep_addr & 0xFu;
if (0 == epn) {
- USB_OTG_FS->EP_CTL[epn] |= USB_ENDPT_EPSTALL_MASK;
+ USB_OTG_FS->EP_CTL[epn] |= USB_ENDPT_EPSTALL_MASK;
} else {
- const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
- buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
- bd[0].bdt_stall = 1;
- bd[1].bdt_stall = 1;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+ bd[0].bdt_stall = 1;
+ bd[1].bdt_stall = 1;
}
}
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
- const unsigned epn = ep_addr & 0xFu;
- const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
- const unsigned odd = _dcd.endpoint[epn][dir].odd;
- buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void)rhport;
+ const unsigned epn = ep_addr & 0xFu;
+ const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ const unsigned odd = _dcd.endpoint[epn][dir].odd;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
bd[odd ^ 1].own = 0;
bd[odd ^ 1].data = 1;
@@ -447,19 +440,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
// ISR
//--------------------------------------------------------------------+
-void dcd_int_handler(uint8_t rhport)
-{
- (void) rhport;
+void dcd_int_handler(uint8_t rhport) {
+ (void)rhport;
- uint32_t is = USB_OTG_FS->INT_STAT;
- uint32_t msk = USB_OTG_FS->INT_ENB;
+ uint32_t is = USB_OTG_FS->INT_STAT;
+ uint32_t msk = USB_OTG_FS->INT_ENB;
USB_OTG_FS->INT_STAT = is & ~msk;
is &= msk;
if (is & USB_ISTAT_ERROR_MASK) {
/* TODO: */
- uint32_t es = USB_OTG_FS->ERR_STAT;
+ uint32_t es = USB_OTG_FS->ERR_STAT;
USB_OTG_FS->ERR_STAT = es;
- USB_OTG_FS->INT_STAT = is; /* discard any pending events */
+ USB_OTG_FS->INT_STAT = is; /* discard any pending events */
return;
}
@@ -493,5 +485,4 @@ void dcd_int_handler(uint8_t rhport)
return;
}
}
-
#endif
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 9e5f5117f..730ca7fb1 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -426,6 +426,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
__DSB();
}
+#if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+#endif
+
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
(void) rhport;
@@ -1062,5 +1077,4 @@ void tusb_hal_nrf_power_event(uint32_t event) {
break;
}
}
-
#endif
diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c
index b0b6fe857..0edebf159 100644
--- a/src/portable/nuvoton/nuc120/dcd_nuc120.c
+++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -275,6 +275,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false; // TODO not implemented yet
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false; // TODO not implemented yet
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c
index f4af97ca7..804a35565 100644
--- a/src/portable/nuvoton/nuc121/dcd_nuc121.c
+++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -38,8 +38,18 @@
#if CFG_TUD_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) )
#include "device/dcd.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
#include "NuMicro.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
#ifndef USE_SOF
@@ -293,8 +303,9 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* construct USB Configuration Register value and then write it */
uint32_t cfg = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
cfg |= (TUSB_DIR_IN == dir) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT;
- if (TUSB_XFER_ISOCHRONOUS == type)
+ if (TUSB_XFER_ISOCHRONOUS == type) {
cfg |= USBD_CFG_TYPE_ISO;
+ }
ep->CFG = cfg;
/* make a note of the endpoint size */
@@ -303,6 +314,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false; // TODO not implemented yet
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false; // TODO not implemented yet
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index 1c98a0a49..6195015ae 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -38,8 +38,18 @@
#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505)
#include "device/dcd.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
#include "NUC505Series.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
/*
* The DMA functionality of the USBD peripheral does not appear to succeed with
* transfer lengths that are longer (> 64 bytes) and are not a multiple of 4.
@@ -357,6 +367,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false; // TODO not implemented yet
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false; // TODO not implemented yet
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index 3d5e195a9..9edaadf58 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -355,24 +355,21 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
- (void) rhport;
+static bool edpt_open(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size, tusb_xfer_type_t xfer) {
+ (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];
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ 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;
+ ep->max_packet_size = max_packet_size;
+ 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;
KHCI->ENDPOINT[epn].ENDPT |= val;
@@ -386,6 +383,26 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
+bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ return edpt_open(rhport, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer);
+}
+
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ return edpt_open(rhport, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) {
+ const unsigned epn = tu_edpt_number(ep_desc->bEndpointAddress);
+ const unsigned dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+
+ dcd_int_disable(rhport);
+ ep->max_packet_size = tu_edpt_packet_size(ep_desc);
+ dcd_int_enable(rhport);
+
+ return true;
+}
+
void dcd_edpt_close_all(uint8_t rhport)
{
(void) rhport;
@@ -408,26 +425,6 @@ void dcd_edpt_close_all(uint8_t rhport)
}
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- 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;
- const unsigned ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- KHCI->ENDPOINT[epn].ENDPT &= ~msk;
- ep->max_packet_size = 0;
- ep->length = 0;
- ep->remaining = 0;
- bd[0].head = 0;
- bd[1].head = 0;
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
-}
-
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
(void) rhport;
@@ -577,5 +574,4 @@ void dcd_int_handler(uint8_t rhport)
process_tokdne(rhport);
}
}
-
#endif
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
index 855c59cd1..5516e1ba6 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
@@ -337,9 +337,17 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport; (void) ep_addr;
- // TODO implement dcd_edpt_close()
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
void dcd_edpt_close_all (uint8_t rhport)
@@ -600,5 +608,4 @@ void dcd_int_handler(uint8_t rhport)
TU_BREAKPOINT();
}
}
-
#endif
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.h b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
index 654b80866..25c9f9985 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DCD_LPC17_40_H_
-#define _TUSB_DCD_LPC17_40_H_
+#ifndef TUSB_DCD_LPC17_40_H_
+#define TUSB_DCD_LPC17_40_H_
#include "common/tusb_common.h"
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index 7c637ce0c..1c135f3be 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -432,6 +432,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
_dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
+#if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+#endif
+
static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) {
uint16_t nbytes;
ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
@@ -631,5 +646,4 @@ void dcd_int_handler(uint8_t rhport)
// Endpoint transfer complete interrupt
process_xfer_isr(rhport, int_status);
}
-
#endif
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index 81091c9a7..5ca093506 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -41,13 +41,16 @@
#include "host/usbh.h"
#include "ohci.h"
-// TODO remove
-#include "chip.h"
+#if defined(TUP_USBIP_OHCI_NXP)
+ #include "ohci_nxp.h"
+#else
+ #error Unsupported OHCI IP
+#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-#define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE)
+
enum {
OHCI_CONTROL_FUNCSTATE_RESET = 0,
@@ -109,22 +112,22 @@ enum {
enum {
OHCI_CCODE_NO_ERROR = 0,
OHCI_CCODE_CRC = 1,
- OHCI_CCODE_BIT_STUFFING = 2,
- OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3,
- OHCI_CCODE_STALL = 4,
- OHCI_CCODE_DEVICE_NOT_RESPONDING = 5,
- OHCI_CCODE_PID_CHECK_FAILURE = 6,
- OHCI_CCODE_UNEXPECTED_PID = 7,
- OHCI_CCODE_DATA_OVERRUN = 8,
- OHCI_CCODE_DATA_UNDERRUN = 9,
- OHCI_CCODE_BUFFER_OVERRUN = 12,
- OHCI_CCODE_BUFFER_UNDERRUN = 13,
- OHCI_CCODE_NOT_ACCESSED = 14,
+ OHCI_CCODE_BIT_STUFFING = 2,
+ OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3,
+ OHCI_CCODE_STALL = 4,
+ OHCI_CCODE_DEVICE_NOT_RESPONDING = 5,
+ OHCI_CCODE_PID_CHECK_FAILURE = 6,
+ OHCI_CCODE_UNEXPECTED_PID = 7,
+ OHCI_CCODE_DATA_OVERRUN = 8,
+ OHCI_CCODE_DATA_UNDERRUN = 9,
+ OHCI_CCODE_BUFFER_OVERRUN = 12,
+ OHCI_CCODE_BUFFER_UNDERRUN = 13,
+ OHCI_CCODE_NOT_ACCESSED = 14,
};
enum {
OHCI_INT_ON_COMPLETE_YES = 0,
- OHCI_INT_ON_COMPLETE_NO = TU_BIN8(111)
+ OHCI_INT_ON_COMPLETE_NO = 0x7 // 0b111
};
enum {
@@ -144,21 +147,36 @@ enum {
};
//--------------------------------------------------------------------+
+// Support for explicit D-cache operations
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK bool hcd_dcache_clean(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
+TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
+
+// Optional macro to access ED in uncached way
+#ifndef hcd_dcache_uncached
+#define hcd_dcache_uncached(x) (x)
+#endif
+
+//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data;
-static ohci_ed_t * const p_ed_head[] =
-{
- [TUSB_XFER_CONTROL] = &ohci_data.control[0].ed,
- [TUSB_XFER_BULK ] = &ohci_data.bulk_head_ed,
- [TUSB_XFER_INTERRUPT] = &ohci_data.period_head_ed,
+static ohci_ed_t * const p_ed_head[] = {
+ [TUSB_XFER_CONTROL] = hcd_dcache_uncached(&ohci_data.control[0].ed),
+ [TUSB_XFER_BULK ] = hcd_dcache_uncached(&ohci_data.bulk_head_ed),
+ [TUSB_XFER_INTERRUPT] = hcd_dcache_uncached(&ohci_data.period_head_ed),
[TUSB_XFER_ISOCHRONOUS] = NULL // TODO Isochronous
};
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
+static ohci_ed_t* ed_from_addr(uint8_t dev_addr, uint8_t ep_addr);
+
+TU_ATTR_ALWAYS_INLINE static inline ohci_ed_t* ed_control(uint8_t daddr) {
+ return hcd_dcache_uncached(&ohci_data.control[daddr].ed);
+}
//--------------------------------------------------------------------+
// USBH-HCD API
@@ -166,16 +184,12 @@ static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
// If your system requires separation of virtual and physical memory, implement
// tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application.
-TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address)
-{
- if (tusb_app_virt_to_phys) return tusb_app_virt_to_phys(virtual_address);
- return virtual_address;
+TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address) {
+ return tusb_app_virt_to_phys(virtual_address);
}
-TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address)
-{
- if (tusb_app_phys_to_virt) return tusb_app_phys_to_virt(physical_address);
- return physical_address;
+TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address) {
+ return tusb_app_phys_to_virt(physical_address);
}
// Initialization according to 5.1.1.4
@@ -183,39 +197,33 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
(void) rh_init;
+ ohci_phy_init(rhport);
+
//------------- Data Structure init -------------//
tu_memclr(&ohci_data, sizeof(ohci_data_t));
- for(uint8_t i=0; i<32; i++)
- { // assign all interrupt pointers to period head ed
+ // assign all interrupt pointers to period head ed
+ for(uint8_t i=0; i<32; i++) {
ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed);
}
- ohci_data.control[0].ed.skip = 1;
- ohci_data.bulk_head_ed.skip = 1;
- ohci_data.period_head_ed.skip = 1;
+ ohci_data.control[0].ed.w0.skip = 1;
+ ohci_data.bulk_head_ed.w0.skip = 1;
+ ohci_data.period_head_ed.w0.skip = 1;
//If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
- if (OHCI_REG->control_bit.interrupt_routing == 1)
- {
+ if (OHCI_REG->control_bit.interrupt_routing == 1) {
OHCI_REG->command_status_bit.ownership_change_request = 1;
while (OHCI_REG->control_bit.interrupt_routing == 1) {}
- }
-
- //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
- else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
- OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL)
- {
+ } else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
+ OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL) {
+ //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
//Wait 20 ms. (Ref Usb spec 7.1.7.7)
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME;
-
-#if CFG_TUSB_OS != OPT_OS_NONE
- // os_none implement task delay using usb frame counter which is not started yet
- // therefore cause infinite delay.
- // TODO find a way to delay in case of os none e.g __nop
- osal_task_delay(20);
-#endif
+ tusb_time_delay_ms_api(20);
}
+ hcd_dcache_clean(&ohci_data, sizeof(ohci_data));
+
// reset controller
OHCI_REG->command_status_bit.controller_reset = 1;
while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us
@@ -241,10 +249,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power)
OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports
-#if CFG_TUSB_OS != OPT_OS_NONE
- // TODO as above delay
- osal_task_delay(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up
-#endif
+ tusb_time_delay_ms_api(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up
return true;
}
@@ -255,7 +260,6 @@ uint32_t hcd_frame_number(uint8_t rhport)
return (ohci_data.frame_number_hi << 16) | OHCI_REG->frame_number;
}
-
//--------------------------------------------------------------------+
// PORT API
//--------------------------------------------------------------------+
@@ -281,26 +285,18 @@ tusb_speed_t hcd_port_speed_get(uint8_t hostid)
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
// thus there is no need to make sure ED is not in HC's cahed as it will not for sure
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
-{
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
// TODO OHCI
(void) rhport;
// addr0 serves as static head --> only set skip bit
- if ( dev_addr == 0 )
- {
- ohci_data.control[0].ed.skip = 1;
- }else
- {
- // remove control
- ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr);
-
- // remove bulk
- ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr);
-
- // remove interrupt
- ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr);
-
+ if (dev_addr == 0) {
+ ohci_ed_t* ed = ed_control(0);
+ ed->w0.skip = 1;
+ } else {
+ ed_list_remove_by_addr(p_ed_head[TUSB_XFER_CONTROL], dev_addr); // remove control
+ ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr); // remove bulk
+ ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr); // remove interrupt
// TODO remove ISO
}
}
@@ -312,35 +308,36 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
//--------------------------------------------------------------------+
// List Helper
//--------------------------------------------------------------------+
-static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_t const * const p_ed)
-{
- return (p_ed->ep_number == 0 ) ? TUSB_XFER_CONTROL :
- (p_ed->is_iso ) ? TUSB_XFER_ISOCHRONOUS :
- (p_ed->is_interrupt_xfer) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
+static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_word0_t w0) {
+ return (w0.ep_number == 0 ) ? TUSB_XFER_CONTROL :
+ (w0.is_iso ) ? TUSB_XFER_ISOCHRONOUS :
+ (w0.is_interrupt_xfer) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK;
}
-static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t ep_addr, uint8_t xfer_type, uint8_t interval)
-{
+static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t ep_addr, uint8_t xfer_type, uint8_t interval) {
(void) interval;
// address 0 is used as async head, which always on the list --> cannot be cleared
- if (dev_addr != 0)
- {
- tu_memclr(p_ed, sizeof(ohci_ed_t));
+ if (dev_addr != 0) {
+ p_ed->td_tail = 0;
+ p_ed->td_head.address = 0;
+ p_ed->next = 0;
}
tuh_bus_info_t bus_info;
tuh_bus_info_get(dev_addr, &bus_info);
- p_ed->dev_addr = dev_addr;
- p_ed->ep_number = ep_addr & 0x0F;
- p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
- p_ed->speed = bus_info.speed;
- p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
- p_ed->max_packet_size = ep_size;
+ ohci_ed_word0_t w0 = {.value = 0};
+ w0.dev_addr = dev_addr;
+ w0.ep_number = ep_addr & 0x0F;
+ w0.pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT);
+ w0.speed = bus_info.speed;
+ w0.is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0;
+ w0.max_packet_size = ep_size;
- p_ed->used = 1;
- p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
+ w0.used = 1;
+ w0.is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
+ p_ed->w0 = w0;
}
static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) {
@@ -363,126 +360,109 @@ static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes)
}
}
-static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
-{
- if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed;
+static ohci_ed_t* ed_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
+ if (tu_edpt_number(ep_addr) == 0) {
+ return ed_control(dev_addr);
+ }
ohci_ed_t* ed_pool = ohci_data.ed_pool;
-
- for(uint32_t i=0; i<ED_MAX; i++)
- {
- if ( (ed_pool[i].dev_addr == dev_addr) &&
- ep_addr == tu_edpt_addr(ed_pool[i].ep_number, ed_pool[i].pid == PID_IN) )
- {
- return &ed_pool[i];
+ for (size_t i = 0; i < ED_MAX; i++) {
+ ohci_ed_t* qhd = hcd_dcache_uncached(&ed_pool[i]);
+ if ((qhd->w0.dev_addr == dev_addr) &&
+ ep_addr == tu_edpt_addr(qhd->w0.ep_number, qhd->w0.pid == PID_IN)) {
+ return qhd;
}
}
return NULL;
}
-static ohci_ed_t * ed_find_free(void)
-{
+static ohci_ed_t* ed_find_free(void) {
ohci_ed_t* ed_pool = ohci_data.ed_pool;
-
- for(uint8_t i = 0; i < ED_MAX; i++)
- {
- if ( !ed_pool[i].used ) return &ed_pool[i];
+ for (size_t i = 0; i < ED_MAX; i++) {
+ ohci_ed_t* qhd = hcd_dcache_uncached(&ed_pool[i]);
+ if (!qhd->w0.used) {
+ return qhd;
+ }
}
-
return NULL;
}
-static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
-{
+static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed) {
p_ed->next = p_pre->next;
p_pre->next = (uint32_t) _phys_addr(p_ed);
}
-static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
-{
+static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) {
ohci_ed_t* p_prev = p_head;
- while( p_prev->next )
- {
- ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
+ while (p_prev->next) {
+ ohci_ed_t* ed = (ohci_ed_t*)_virt_addr((void*)p_prev->next);
- if (ed->dev_addr == dev_addr)
- {
+ if (ed->w0.dev_addr == dev_addr) {
// Prevent Host Controller from processing this ED while we remove it
- ed->skip = 1;
+ ed->w0.skip = 1;
// unlink ed, will also move up p_prev
p_prev->next = ed->next;
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
- ed->next = (uint32_t) _phys_addr(p_head);
- ed->used = 0;
- ed->skip = 0;
- }else
- {
- p_prev = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
+ ed->next = (uint32_t)_phys_addr(p_head);
+ ed->w0.used = 0;
+ ed->w0.skip = 0;
+ } else {
+ p_prev = (ohci_ed_t*)_virt_addr((void*)p_prev->next);
}
}
}
-static ohci_gtd_t * gtd_find_free(void)
-{
- for(uint8_t i=0; i < GTD_MAX; i++)
- {
- if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i];
+static ohci_gtd_t* gtd_find_free(void) {
+ for (uint8_t i = 0; i < GTD_MAX; i++) {
+ if (!ohci_data.gtd_pool[i].used) {
+ return &ohci_data.gtd_pool[i];
+ }
}
-
return NULL;
}
-static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
-{
- // tail is always NULL
- if ( tu_align16(p_ed->td_head.address) == 0 )
- { // TD queue is empty --> head = TD
- p_ed->td_head.address |= (uint32_t) _phys_addr(p_gtd);
- }
- else
- { // TODO currently only support queue up to 2 TD each endpoint at a time
- ((ohci_gtd_t*) tu_align16((uint32_t)_virt_addr((void *)p_ed->td_head.address)))->next = (uint32_t) _phys_addr(p_gtd);
- }
-}
-
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
-{
- (void) rhport;
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc) {
+ (void)rhport;
// TODO iso support
TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
//------------- Prepare Queue Head -------------//
- ohci_ed_t * p_ed;
-
- if ( ep_desc->bEndpointAddress == 0 )
- {
- p_ed = &ohci_data.control[dev_addr].ed;
- }else
- {
+ ohci_ed_t* p_ed;
+ if (ep_desc->bEndpointAddress == 0) {
+ p_ed = ed_control(dev_addr);
+ } else {
p_ed = ed_find_free();
}
TU_ASSERT(p_ed);
- ed_init( p_ed, dev_addr, tu_edpt_packet_size(ep_desc), ep_desc->bEndpointAddress,
- ep_desc->bmAttributes.xfer, ep_desc->bInterval );
+ ed_init(p_ed, dev_addr, tu_edpt_packet_size(ep_desc), ep_desc->bEndpointAddress,
+ ep_desc->bmAttributes.xfer, ep_desc->bInterval);
// control of dev0 is used as static async head
- if ( dev_addr == 0 )
- {
- p_ed->skip = 0; // only need to clear skip bit
+ if (dev_addr == 0) {
+ p_ed->w0.skip = 0; // only need to clear skip bit
return true;
}
- ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed );
+ if (tu_edpt_number(ep_desc->bEndpointAddress) != 0) {
+ // Get an empty TD and use it as the end-of-list marker.
+ // This marker TD will be used when a transfer is made on this EP
+ // (and a new, empty TD will be allocated for the next-next transfer).
+ ohci_gtd_t* gtd = gtd_find_free();
+ TU_ASSERT(gtd);
+ p_ed->td_head.address = (uint32_t)_phys_addr(gtd);
+ p_ed->td_tail = (uint32_t)_phys_addr(gtd);
+ }
+ ed_list_insert(p_ed_head[ep_desc->bmAttributes.xfer], p_ed);
return true;
}
@@ -491,18 +471,20 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
return false; // TODO not implemented yet
}
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
-{
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
(void) rhport;
- ohci_ed_t* ed = &ohci_data.control[dev_addr].ed;
+ ohci_ed_t* ed = ed_control(dev_addr);
ohci_gtd_t *qtd = &ohci_data.control[dev_addr].gtd;
+ hcd_dcache_clean(setup_packet, 8);
+
gtd_init(qtd, (uint8_t*)(uintptr_t) setup_packet, 8);
qtd->index = dev_addr;
qtd->pid = PID_SETUP;
qtd->data_toggle = GTD_DT_DATA0;
qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+ hcd_dcache_clean(qtd, sizeof(ohci_gtd_t));
//------------- Attach TDs list to Control Endpoint -------------//
ed->td_head.address = (uint32_t) _phys_addr(qtd);
@@ -512,43 +494,53 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
return true;
}
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
-{
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
- if ( epnum == 0 )
- {
- ohci_ed_t* ed = &ohci_data.control[dev_addr].ed;
- ohci_gtd_t* gtd = &ohci_data.control[dev_addr].gtd;
+ // IN transfer: invalidate buffer, OUT transfer: clean buffer
+ if (dir) {
+ hcd_dcache_invalidate(buffer, buflen);
+ } else {
+ hcd_dcache_clean(buffer, buflen);
+ }
+ ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr);
+ TU_ASSERT(ed);
+ if (epnum == 0) {
+ ohci_gtd_t* gtd = &ohci_data.control[dev_addr].gtd;
gtd_init(gtd, buffer, buflen);
-
gtd->index = dev_addr;
- gtd->pid = dir ? PID_IN : PID_OUT;
- gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1
+ gtd->pid = dir ? PID_IN : PID_OUT;
+ gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
+ hcd_dcache_clean(gtd, sizeof(ohci_gtd_t));
- ed->td_head.address = (uint32_t) _phys_addr(gtd);
+ ed->td_head.address = (uint32_t)_phys_addr(gtd);
OHCI_REG->command_status_bit.control_list_filled = 1;
- }else
- {
- ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr);
- ohci_gtd_t* gtd = gtd_find_free();
-
- TU_ASSERT(gtd);
+ } else {
+ tusb_xfer_type_t xfer_type = ed_get_xfer_type(ed->w0);
+ ohci_gtd_t* gtd = (ohci_gtd_t*)_virt_addr((void*)ed->td_tail);
gtd_init(gtd, buffer, buflen);
gtd->index = ed-ohci_data.ed_pool;
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
- td_insert_to_ed(ed, gtd);
+ // Insert a new, empty TD at the tail, to be used by the next transfer
+ ohci_gtd_t* new_gtd = gtd_find_free();
+ TU_ASSERT(new_gtd);
+
+ gtd->next = (uint32_t)_phys_addr(new_gtd);
+ hcd_dcache_clean(gtd, sizeof(ohci_gtd_t));
- tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
- if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
+ ed->td_tail = (uint32_t)_phys_addr(new_gtd);
+
+ if (TUSB_XFER_BULK == xfer_type) {
+ OHCI_REG->command_status_bit.bulk_list_filled = 1;
+ }
}
return true;
@@ -566,13 +558,14 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);
- p_ed->is_stalled = 0;
- p_ed->td_tail &= 0x0Ful; // set tail pointer back to NULL
+ ohci_ed_word2_t td_head = p_ed->td_head;
+ td_head.toggle = 0; // reset data toggle
+ td_head.halted = 0;
+ p_ed->td_head = td_head;
- p_ed->td_head.toggle = 0; // reset data toggle
- p_ed->td_head.halted = 0;
-
- if ( TUSB_XFER_BULK == ed_get_xfer_type(p_ed) ) OHCI_REG->command_status_bit.bulk_list_filled = 1;
+ if (TUSB_XFER_BULK == ed_get_xfer_type(p_ed->w0)) {
+ OHCI_REG->command_status_bit.bulk_list_filled = 1;
+ }
return true;
}
@@ -581,14 +574,18 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
//--------------------------------------------------------------------+
// OHCI Interrupt Handler
//--------------------------------------------------------------------+
-static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
-{
- ohci_td_item_t* td_reverse_head = NULL;
+TU_ATTR_ALWAYS_INLINE static inline bool is_itd(ohci_td_item_t* item) {
+ (void) item;
+ return false; // ISO not supported yet
+}
- while(td_head != NULL)
- {
+static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head) {
+ ohci_td_item_t* td_reverse_head = NULL;
+ while(td_head != NULL) {
td_head = _virt_addr(td_head);
- uint32_t next = td_head->next;
+ const uint32_t item_size = is_itd(td_head) ? sizeof(ohci_itd_t) : sizeof(ohci_gtd_t);
+ hcd_dcache_invalidate(td_head, item_size);
+ const uint32_t next = td_head->next;
// make current's item become reverse's first item
td_head->next = (uint32_t) td_reverse_head;
@@ -600,24 +597,22 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
return _virt_addr(td_reverse_head);
}
-static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) {
return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout
}
-static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
-{
- if ( gtd_is_control(p_qtd) )
- {
- return &ohci_data.control[p_qtd->index].ed;
- }else
- {
- return &ohci_data.ed_pool[p_qtd->index];
+TU_ATTR_ALWAYS_INLINE static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const* const p_qtd) {
+ ohci_ed_t* ed;
+ if (gtd_is_control(p_qtd)) {
+ ed = &ohci_data.control[p_qtd->index].ed;
+ } else {
+ ed = &ohci_data.ed_pool[p_qtd->index];
}
+ return hcd_dcache_uncached(ed);
}
static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
- if ( gtd_is_control(gtd) ) {
+ if (gtd_is_control(gtd)) {
uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
return &ohci_data.gtd_extra_control[idx];
}else {
@@ -625,102 +620,77 @@ static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
}
}
-static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) {
// 5.2.9 OHCI sample code
-
// CBP is 0 mean all data is transferred
- if (current_buffer == 0) return 0;
+ if (current_buffer == 0) {
+ return 0;
+ }
return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) +
- tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1;
+ tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1;
}
-static void done_queue_isr(uint8_t hostid)
-{
- (void) hostid;
+static void done_queue_isr(uint8_t hostid) {
+ (void)hostid;
// done head is written in reversed order of completion --> need to reverse the done queue first
- ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
+ ohci_td_item_t* td_head = list_reverse((ohci_td_item_t*)tu_align16(ohci_data.hcca.done_head));
ohci_data.hcca.done_head = 0;
- while( td_head != NULL )
- {
+ while (td_head != NULL) {
// TODO check if td_head is iso td
//------------- Non ISO transfer -------------//
- ohci_gtd_t * const qtd = (ohci_gtd_t *) td_head;
+ ohci_gtd_t* const qtd = (ohci_gtd_t*) td_head;
xfer_result_t const event = (qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS :
(qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED;
-
qtd->used = 0; // free TD
- if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
- {
- ohci_ed_t * const ed = gtd_get_ed(qtd);
- uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
-
- // NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
- // When there is a error resulting this ED is halted, and this EP still has other queued TD
- // --> the Bulk list only has this halted EP queueing TDs (remaining)
- // --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list
- // --> HC will not process Control list (due to service ratio when Bulk list not empty)
- // To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
- // the TailP must be set back to NULL for processing remaining TDs
- if (event != XFER_RESULT_SUCCESS)
- {
- ed->td_tail &= 0x0Ful;
- ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
- if ( event == XFER_RESULT_STALLED ) ed->is_stalled = 1;
- }
-
- uint8_t dir = (ed->ep_number == 0) ? (qtd->pid == PID_IN) : (ed->pid == PID_IN);
-
- hcd_event_xfer_complete(ed->dev_addr, tu_edpt_addr(ed->ep_number, dir), xferred_bytes, event, true);
+ if ((qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS)) {
+ const ohci_ed_t* ed = gtd_get_ed(qtd);
+ const ohci_ed_word0_t ed_w0 = ed->w0;
+ const uint32_t xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t)qtd->buffer_end, (uint32_t)qtd->current_buffer_pointer);
+ uint8_t dir = (ed_w0.ep_number == 0) ? (qtd->pid == PID_IN) : (ed_w0.pid == PID_IN);
+ const uint8_t ep_addr = tu_edpt_addr(ed_w0.ep_number, dir);
+ hcd_event_xfer_complete(ed_w0.dev_addr, ep_addr, xferred_bytes, event, true);
}
- td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next);
+ td_head = (ohci_td_item_t*)_virt_addr((void*)td_head->next);
}
}
void hcd_int_handler(uint8_t hostid, bool in_isr) {
- (void) in_isr;
-
- uint32_t const int_en = OHCI_REG->interrupt_enable;
+ (void)in_isr;
+ uint32_t const int_en = OHCI_REG->interrupt_enable;
uint32_t const int_status = OHCI_REG->interrupt_status & int_en;
- if (int_status == 0) return;
+ if (int_status == 0) {
+ return;
+ }
// Disable MIE as per OHCI spec 5.3
OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK;
// Frame number overflow
- if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
- {
+ if (int_status & OHCI_INT_FRAME_OVERFLOW_MASK) {
ohci_data.frame_number_hi++;
}
//------------- RootHub status -------------//
- if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
- {
- for (int i = 0; i < TUP_OHCI_RHPORTS; i++)
- {
+ if (int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK) {
+ for (int i = 0; i < TUP_OHCI_RHPORTS; i++) {
uint32_t const rhport_status = OHCI_REG->rhport_status[i] & RHPORT_ALL_CHANGE_MASK;
- if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
- {
+ if (rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK) {
// TODO check if remote wake-up
- if ( OHCI_REG->rhport_status_bit[i].current_connect_status )
- {
+ if (OHCI_REG->rhport_status_bit[i].current_connect_status) {
// TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
OHCI_REG->rhport_status[i] = RHPORT_PORT_RESET_STATUS_MASK;
hcd_event_device_attach(i, true);
- }else
- {
+ } else {
hcd_event_device_remove(i, true);
}
}
- if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
- {
-
+ if (rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK) {
}
OHCI_REG->rhport_status[i] = rhport_status; // acknowledge all interrupt
@@ -728,13 +698,11 @@ void hcd_int_handler(uint8_t hostid, bool in_isr) {
}
//------------- Transfer Complete -------------//
- if (int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK)
- {
+ if (int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK) {
done_queue_isr(hostid);
}
OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt
-
OHCI_REG->interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK; // Enable MIE
}
//--------------------------------------------------------------------+
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index 94bad5df7..d9ebe1d54 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OHCI_H_
-#define _TUSB_OHCI_H_
+#ifndef TUSB_OHCI_H_
+#define TUSB_OHCI_H_
#ifdef __cplusplus
extern "C" {
@@ -48,6 +48,10 @@ enum {
// tinyUSB's OHCI implementation caps number of EDs to 8 bits
TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
+#define GTD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)
+#define ED_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)
+#define ITD_ALIGN_SIZE TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 32)
+
//--------------------------------------------------------------------+
// OHCI Data Structure
//--------------------------------------------------------------------+
@@ -61,18 +65,35 @@ typedef struct {
TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
+// An OHCI host controller is controlled using data structures placed in memory (RAM).
+// It needs to both read and write these data structures (as defined by the OHCI specification),
+// and this can be mentally conceptualized similar to two software threads running on
+// two different CPUs. In order to prevent a _data race_ where data gets corrupted,
+// the CPU and the OHCI host controller need to agree on how the memory should be accessed.
+// In this driver, we do this by transferring logical ownership of transfer descriptors (TDs)
+// between the CPU and the OHCI host controller. Only the device which holds the logical ownership
+// is allowed to read or write the TD. This ownership is not visible anywhere in the code,
+// but it instead must be inferred based on the logical state of the transfer.
+//
+// If dcache-supporting mode is enabled, we need to do additional manual cache operations
+// in order to correctly transfer this logical ownership and prevent data corruption.
+// In order to do this, we also choose to align each OHCI TD so that it doesn't
+// share CPU cache lines with other TDs. This is because manual cache operations
+// can only be performed on cache line granularity. In other words, one cache line is
+// the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs
+// in the same cache line, they would be required to always have the same logical ownership.
+// This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely.
+
// common link item for gtd and itd for list travel
-// use as pointer only
typedef struct TU_ATTR_ALIGNED(16) {
uint32_t reserved[2];
volatile uint32_t next;
uint32_t reserved2;
}ohci_td_item_t;
-typedef struct TU_ATTR_ALIGNED(16)
-{
- // Word 0
- uint32_t used : 1;
+typedef struct TU_ATTR_ALIGNED(GTD_ALIGN_SIZE) {
+ // Word 0
+ uint32_t used : 1;
uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
uint32_t : 9; // can be used
uint32_t buffer_rounding : 1;
@@ -82,56 +103,55 @@ typedef struct TU_ATTR_ALIGNED(16)
volatile uint32_t error_count : 2;
volatile uint32_t condition_code : 4;
- // Word 1
- uint8_t* volatile current_buffer_pointer;
+ // Word 1
+ uint8_t* volatile current_buffer_pointer;
- // Word 2 : next TD
- volatile uint32_t next;
+ // Word 2 : next TD
+ volatile uint32_t next;
- // Word 3
- uint8_t* buffer_end;
+ // Word 3
+ uint8_t* buffer_end;
} ohci_gtd_t;
+TU_VERIFY_STATIC(sizeof(ohci_gtd_t) == GTD_ALIGN_SIZE, "size is not correct" );
-TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == 16, "size is not correct" );
-
-typedef struct TU_ATTR_ALIGNED(16)
-{
- // Word 0
- uint32_t dev_addr : 7;
- uint32_t ep_number : 4;
- uint32_t pid : 2;
- uint32_t speed : 1;
- uint32_t skip : 1;
- uint32_t is_iso : 1;
- uint32_t max_packet_size : 11;
- // HCD: make use of 5 reserved bits
- uint32_t used : 1;
- uint32_t is_interrupt_xfer : 1;
- uint32_t is_stalled : 1;
- uint32_t : 2;
-
- // Word 1
- uint32_t td_tail;
+typedef union {
+ struct {
+ uint32_t dev_addr : 7;
+ uint32_t ep_number : 4;
+ uint32_t pid : 2;
+ uint32_t speed : 1;
+ uint32_t skip : 1;
+ uint32_t is_iso : 1;
+ uint32_t max_packet_size : 11;
+ // HCD: make use of 5 reserved bits
+ uint32_t used : 1;
+ uint32_t is_interrupt_xfer : 1;
+ uint32_t : 3;
+ };
+ uint32_t value;
+} ohci_ed_word0_t;
+TU_VERIFY_STATIC(sizeof(ohci_ed_word0_t) == 4, "size is not correct" );
- // Word 2
- volatile union {
- uint32_t address;
- struct {
- uint32_t halted : 1;
- uint32_t toggle : 1;
- uint32_t : 30;
- };
- }td_head;
+typedef union {
+ uint32_t address;
+ struct {
+ uint32_t halted : 1;
+ uint32_t toggle : 1;
+ uint32_t : 30;
+ };
+} ohci_ed_word2_t;
+TU_VERIFY_STATIC(sizeof(ohci_ed_word2_t) == 4, "size is not correct" );
- // Word 3: next ED
- uint32_t next;
+typedef struct TU_ATTR_ALIGNED(ED_ALIGN_SIZE) {
+ ohci_ed_word0_t w0; // Word 0
+ uint32_t td_tail; // Word 1
+ volatile ohci_ed_word2_t td_head; // Word 2
+ uint32_t next; // Word 3
} ohci_ed_t;
+TU_VERIFY_STATIC(sizeof(ohci_ed_t) == ED_ALIGN_SIZE, "size is not correct" );
-TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" );
-
-typedef struct TU_ATTR_ALIGNED(32)
-{
- /*---------- Word 1 ----------*/
+typedef struct TU_ATTR_ALIGNED(ITD_ALIGN_SIZE) {
+ /*---------- Word 1 ----------*/
uint32_t starting_frame : 16;
uint32_t : 5; // can be used
uint32_t delay_interrupt : 3;
@@ -139,24 +159,25 @@ typedef struct TU_ATTR_ALIGNED(32)
uint32_t : 1; // can be used
volatile uint32_t condition_code : 4;
- /*---------- Word 2 ----------*/
- uint32_t buffer_page0; // 12 lsb bits can be used
- /*---------- Word 3 ----------*/
- volatile uint32_t next;
+ /*---------- Word 2 ----------*/
+ uint32_t buffer_page0; // 12 lsb bits can be used
- /*---------- Word 4 ----------*/
- uint32_t buffer_end;
+ /*---------- Word 3 ----------*/
+ volatile uint32_t next;
- /*---------- Word 5-8 ----------*/
- volatile uint16_t offset_packetstatus[8];
-} ochi_itd_t;
+ /*---------- Word 4 ----------*/
+ uint32_t buffer_end;
-TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
+ /*---------- Word 5-8 ----------*/
+ volatile uint16_t offset_packetstatus[8];
+} ohci_itd_t;
+TU_VERIFY_STATIC(sizeof(ohci_itd_t) == ITD_ALIGN_SIZE, "size is not correct" );
typedef struct {
uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
} gtd_extra_data_t;
+TU_VERIFY_STATIC(sizeof(gtd_extra_data_t) == 2, "size is not correct" );
// structure with member alignment required from large to small
typedef struct TU_ATTR_ALIGNED(256) {
@@ -192,10 +213,10 @@ typedef struct TU_ATTR_ALIGNED(256) {
//--------------------------------------------------------------------+
typedef volatile struct
{
- uint32_t revision;
+ uint32_t revision; // 0x00
union {
- uint32_t control;
+ uint32_t control; // 0x04
struct {
uint32_t control_bulk_service_ratio : 2;
uint32_t periodic_list_enable : 1;
@@ -211,7 +232,7 @@ typedef volatile struct
};
union {
- uint32_t command_status;
+ uint32_t command_status; // 0x08
struct {
uint32_t controller_reset : 1;
uint32_t control_list_filled : 1;
@@ -222,26 +243,24 @@ typedef volatile struct
}command_status_bit;
};
- uint32_t interrupt_status;
- uint32_t interrupt_enable;
- uint32_t interrupt_disable;
-
- uint32_t hcca;
- uint32_t period_current_ed;
- uint32_t control_head_ed;
- uint32_t control_current_ed;
- uint32_t bulk_head_ed;
- uint32_t bulk_current_ed;
- uint32_t done_head;
-
- uint32_t frame_interval;
- uint32_t frame_remaining;
- uint32_t frame_number;
- uint32_t periodic_start;
- uint32_t lowspeed_threshold;
+ uint32_t interrupt_status; // 0x0C
+ uint32_t interrupt_enable; // 0x10
+ uint32_t interrupt_disable; // 0x14
+ uint32_t hcca; // 0x18
+ uint32_t period_current_ed; // 0x1C
+ uint32_t control_head_ed; // 0x20
+ uint32_t control_current_ed; // 0x24
+ uint32_t bulk_head_ed; // 0x28
+ uint32_t bulk_current_ed; // 0x2C
+ uint32_t done_head; // 0x30
+ uint32_t frame_interval; // 0x34
+ uint32_t frame_remaining; // 0x38
+ uint32_t frame_number; // 0x3C
+ uint32_t periodic_start; // 0x40
+ uint32_t lowspeed_threshold; // 0x44
union {
- uint32_t rh_descriptorA;
+ uint32_t rh_descriptorA; // 0x48
struct {
uint32_t number_downstream_ports : 8;
uint32_t power_switching_mode : 1;
@@ -255,7 +274,7 @@ typedef volatile struct
};
union {
- uint32_t rh_descriptorB;
+ uint32_t rh_descriptorB; // 0x4C
struct {
uint32_t device_removable : 16;
uint32_t port_power_control_mask : 16;
@@ -263,9 +282,9 @@ typedef volatile struct
};
union {
- uint32_t rh_status;
+ uint32_t rh_status; // 0x50
struct {
- uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power
+ uint32_t local_power_status : 1; // read Local Power Status; write: Clear Global Power
uint32_t over_current_indicator : 1;
uint32_t : 13;
uint32_t device_remote_wakeup_enable : 1;
@@ -277,7 +296,8 @@ typedef volatile struct
};
union {
- uint32_t rhport_status[TUP_OHCI_RHPORTS];
+ uint32_t rhport_status[TUP_OHCI_RHPORTS]; // 0x54
+
struct {
uint32_t current_connect_status : 1;
uint32_t port_enable_status : 1;
@@ -304,4 +324,4 @@ TU_VERIFY_STATIC( sizeof(ohci_registers_t) == (0x54 + (4 * TUP_OHCI_RHPORTS)), "
}
#endif
-#endif /* _TUSB_OHCI_H_ */
+#endif /* TUSB_OHCI_H_ */
diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/ohci/ohci_nxp.h
index fea3e2a66..9cba05e95 100644
--- a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+++ b/src/portable/ohci/ohci_nxp.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2019, Ha Thach (tinyusb.org)
+ * 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
@@ -23,26 +23,48 @@
*
* This file is part of the TinyUSB stack.
*/
+#ifndef TUSB_OHCI_NXP_H
+#define TUSB_OHCI_NXP_H
-#include "tusb_option.h"
-
-#if CFG_TUH_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
+#if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
#include "chip.h"
-#include "host/hcd.h"
-#include "host/usbh.h"
+#define OHCI_REG ((ohci_registers_t *) LPC_USB_BASE)
-void hcd_int_enable(uint8_t rhport)
-{
- (void) rhport;
+void hcd_int_enable(uint8_t rhport) {
+ (void)rhport;
NVIC_EnableIRQ(USB_IRQn);
}
-void hcd_int_disable(uint8_t rhport)
-{
- (void) rhport;
+void hcd_int_disable(uint8_t rhport) {
+ (void)rhport;
NVIC_DisableIRQ(USB_IRQn);
}
+static void ohci_phy_init(uint8_t rhport) {
+ (void) rhport;
+}
+
+#else
+
+#include "fsl_device_registers.h"
+
+// for LPC55 USB0 controller
+#define OHCI_REG ((ohci_registers_t *) USBFSH_BASE)
+
+static void ohci_phy_init(uint8_t rhport) {
+ (void) rhport;
+}
+
+void hcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+ NVIC_EnableIRQ(USB0_IRQn);
+}
+
+void hcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+ NVIC_DisableIRQ(USB0_IRQn);
+}
+#endif
+
#endif
diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
index 60afbd435..25ef117c2 100644
--- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
@@ -113,6 +113,19 @@ void dcd_edpt_close_all (uint8_t rhport)
(void) rhport;
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+
// 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)
{
@@ -207,5 +220,4 @@ void __no_inline_not_in_flash_func(pio_usb_device_irq_handler)(uint8_t root_id)
// clear all
rport->ints &= ~ints;
}
-
#endif
diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
index d59a2b4ee..90eb920e0 100644
--- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
@@ -29,9 +29,20 @@
#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && CFG_TUH_RPI_PIO_USB
#include "pico.h"
+
#include "pio_usb.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
#include "pio_usb_ll.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 358ce84bc..a89c2f42b 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -190,8 +190,9 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
bool done = hw_endpoint_xfer_continue(ep);
if (done) {
// Notify
- dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
+ const uint16_t xferred_len = ep->xferred_len;
hw_endpoint_reset_transfer(ep);
+ dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true);
}
remaining_buffers &= ~bit;
}
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index ecd28973c..2cc79aa74 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -859,6 +859,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
_dcd.ep[dir][epn] = 0;
}
+#if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+#endif
+
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
{
rusb2_reg_t* rusb = RUSB2_REG(rhport);
@@ -1028,5 +1043,4 @@ void dcd_int_handler(uint8_t rhport)
}
}
}
-
#endif
diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c
index 72e65736b..856f9714f 100644
--- a/src/portable/renesas/rusb2/rusb2_common.c
+++ b/src/portable/renesas/rusb2/rusb2_common.c
@@ -50,10 +50,6 @@ void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) {
rusb2_controller[rhport].irqnum = irqnum;
}
-// void osal_task_delay(uint32_t msec) {
-// R_BSP_SoftwareDelay(msec, BSP_DELAY_UNITS_MILLISECONDS);
-// }
-
#else
#error "Unsupported MCU"
#endif
diff --git a/src/portable/renesas/rusb2/rusb2_type.h b/src/portable/renesas/rusb2/rusb2_type.h
index dd88f66a7..71837d03c 100644
--- a/src/portable/renesas/rusb2/rusb2_type.h
+++ b/src/portable/renesas/rusb2/rusb2_type.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_RUSB2_TYPE_H_
-#define _TUSB_RUSB2_TYPE_H_
+#ifndef TUSB_RUSB2_TYPE_H_
+#define TUSB_RUSB2_TYPE_H_
#include <stdint.h>
#include <stddef.h>
@@ -1777,4 +1777,4 @@ TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR1R_FS ) == 0x0404, "incorrect offset
}
#endif
-#endif /* _TUSB_RUSB2_TYPE_H_ */
+#endif /* TUSB_RUSB2_TYPE_H_ */
diff --git a/src/portable/sony/cxd56/dcd_cxd56.c b/src/portable/sony/cxd56/dcd_cxd56.c
index b16509c6f..a13cd152c 100644
--- a/src/portable/sony/cxd56/dcd_cxd56.c
+++ b/src/portable/sony/cxd56/dcd_cxd56.c
@@ -339,6 +339,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
return true;
}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false; // TODO not implemented yet
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false; // TODO not implemented yet
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index ef58957bb..ed823a832 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -117,6 +117,8 @@
#include "fsdev_stm32.h"
#elif defined(TUP_USBIP_FSDEV_CH32)
#include "fsdev_ch32.h"
+#elif defined(TUP_USBIP_FSDEV_AT32)
+ #include "fsdev_at32.h"
#else
#error "Unknown USB IP"
#endif
@@ -331,8 +333,13 @@ static void handle_ctr_rx(uint32_t ep_id) {
xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
uint8_t buf_id;
- if (is_iso) {
- buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered
+#if FSDEV_USE_SBUF_ISO == 0
+ bool const dbl_buf = is_iso;
+#else
+ bool const dbl_buf = false;
+#endif
+ if (dbl_buf) {
+ buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1;
} else {
buf_id = BTABLE_BUF_RX;
}
@@ -527,10 +534,16 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
return i;
}
+#if FSDEV_USE_SBUF_ISO == 0
+ bool const dbl_buf = ep_type == TUSB_XFER_ISOCHRONOUS;
+#else
+ bool const dbl_buf = false;
+#endif
+
// If EP of current direction is not allocated
- // Except for ISO endpoint, both direction should be free
+ // For double-buffered mode both directions needs to be free
if (!ep_alloc_status[i].allocated[dir] &&
- (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) {
+ (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) {
// Check if EP number is the same
if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) {
// One EP pair has to be the same type
@@ -652,9 +665,7 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
uint8_t const dir = tu_edpt_dir(ep_addr);
uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS);
- /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA,
- for smaller devices double buffering occupy too much space. */
-#if FSDEV_PMA_SIZE > 1024u
+#if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0
uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
uint16_t pma_addr2 = pma_addr >> 16;
#else
@@ -662,8 +673,13 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
uint16_t pma_addr2 = pma_addr;
#endif
+#if FSDEV_USE_SBUF_ISO == 0
btable_set_addr(ep_idx, 0, pma_addr);
btable_set_addr(ep_idx, 1, pma_addr2);
+#else
+ btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
+ (void) pma_addr2;
+#endif
xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
xfer->ep_idx = ep_idx;
@@ -684,10 +700,23 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep)
uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK;
ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_TX | USB_EP_CTR_RX;
+#if FSDEV_USE_SBUF_ISO != 0
+ ep_reg |= USB_EP_KIND;
+
+ ep_change_status(&ep_reg, dir, EP_STAT_DISABLED);
+ ep_change_dtog(&ep_reg, dir, 0);
+
+ if (dir == TUSB_DIR_IN) {
+ ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX);
+ } else {
+ ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX);
+ }
+#else
ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED);
ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED);
ep_change_dtog(&ep_reg, dir, 0);
ep_change_dtog(&ep_reg, (tusb_dir_t)(1 - dir), 1);
+#endif
ep_write(ep_idx, ep_reg, true);
@@ -702,7 +731,12 @@ static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
bool const is_iso = ep_is_iso(ep_reg);
uint8_t buf_id;
- if (is_iso) {
+#if FSDEV_USE_SBUF_ISO == 0
+ bool const dbl_buf = is_iso;
+#else
+ bool const dbl_buf = false;
+#endif
+ if (dbl_buf) {
buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0;
} else {
buf_id = BTABLE_BUF_TX;
diff --git a/src/portable/st/stm32_fsdev/fsdev_at32.h b/src/portable/st/stm32_fsdev/fsdev_at32.h
new file mode 100644
index 000000000..deb1de2a8
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_at32.h
@@ -0,0 +1,225 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+#ifndef TUSB_FSDEV_AT32_H
+#define TUSB_FSDEV_AT32_H
+
+#include "common/tusb_compiler.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_AT32F403A_407
+ #include "at32f403a_407.h"
+
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F413
+ #include "at32f413.h"
+
+#endif
+
+#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_USE_SBUF_ISO 0
+#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
+#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
+
+#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP
+ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0
+#endif
+
+/**************************** ISTR interrupt events *************************/
+#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */
+#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */
+#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */
+#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */
+#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */
+#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */
+#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */
+#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */
+#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */
+#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */
+
+/* Legacy defines */
+#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR
+
+#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */
+#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/
+#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */
+#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */
+#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */
+#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */
+#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */
+#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */
+
+/* Legacy defines */
+#define USB_CLR_PMAOVRM USB_CLR_PMAOVR
+
+/************************* CNTR control register bits definitions ***********/
+#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */
+#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */
+#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */
+#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */
+#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */
+#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */
+#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */
+#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */
+#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */
+#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */
+#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */
+#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */
+#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */
+
+/* Legacy defines */
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR
+#define USB_CNTR_LP_MODE USB_CNTR_LPMODE
+
+/******************** FNR Frame Number Register bit definitions ************/
+#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */
+#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */
+#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */
+#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */
+#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */
+
+/******************** DADDR Device ADDRess bit definitions ****************/
+#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */
+#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */
+
+/****************************** Endpoint register *************************/
+#define USB_EP0R USB_BASE /*!< endpoint 0 register address */
+#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */
+#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */
+#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */
+#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */
+#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */
+#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */
+#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */
+/* bit positions */
+#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */
+#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */
+#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */
+#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */
+#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */
+#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */
+#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */
+#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */
+#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */
+#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */
+
+/* EndPoint REGister MASK (no toggle fields) */
+#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+ /*!< EP_TYPE[1:0] EndPoint TYPE */
+#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */
+#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */
+#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */
+#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */
+#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */
+#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK)
+
+#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */
+ /*!< STAT_TX[1:0] STATus for TX transfer */
+#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */
+#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */
+#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */
+#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */
+#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */
+#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */
+#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK)
+ /*!< STAT_RX[1:0] STATus for RX transfer */
+#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */
+#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */
+#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */
+#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */
+#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
+
+#include "fsdev_type.h"
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+static const IRQn_Type fsdev_irq[] = {
+ USBFS_H_CAN1_TX_IRQn,
+ USBFS_L_CAN1_RX0_IRQn,
+ USBFSWakeUp_IRQn
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+
+#else
+ #error "Unsupported MCU"
+#endif
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+ #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+ // AT32F403A/407 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (CRM->intmap_bit.usbintmap) {
+ NVIC_EnableIRQ(USBFS_MAPH_IRQn);
+ NVIC_EnableIRQ(USBFS_MAPL_IRQn);
+ NVIC_EnableIRQ(USBFSWakeUp_IRQn);
+ } else
+ #endif
+ {
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+ #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413)
+ // AT32F403A/407 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (CRM->intmap_bit.usbintmap) {
+ NVIC_DisableIRQ(USBFS_MAPH_IRQn);
+ NVIC_DisableIRQ(USBFS_MAPL_IRQn);
+ NVIC_DisableIRQ(USBFSWakeUp_IRQn);
+ } else
+ #endif
+ {
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ /* disable usb phy */
+ FSDEV_REG->CNTR |= USB_CNTR_PDWN;
+ /* D+ 1.5k pull-up disable, USB->cfg_bit.puo = TRUE; */
+ *(uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ /* enable usb phy */
+ FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
+ /* Dp 1.5k pull-up enable, USB->cfg_bit.puo = 0; */
+ *(uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1);
+}
+
+#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h
index 518197c47..ceebb6dab 100644
--- a/src/portable/st/stm32_fsdev/fsdev_ch32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h
@@ -54,9 +54,14 @@
#endif
#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_USE_SBUF_ISO 0
#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
+#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP
+ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0
+#endif
+
/**************************** ISTR interrupt events *************************/
#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */
#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index ccf31e035..30ffadc35 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -36,6 +36,7 @@
#include "stm32f0xx.h"
#define FSDEV_PMA_SIZE (1024u)
#define FSDEV_REG_BASE USB_BASE
+ #define FSDEV_HAS_SBUF_ISO 0
// F0x2 models are crystal-less
// All have internal D+ pull-up
// 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
@@ -44,6 +45,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
#include "stm32f1xx.h"
#define FSDEV_PMA_SIZE (512u)
+ #define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *B, and *C: 2 x 16 bits/word
@@ -55,6 +57,7 @@
defined(STM32F373xC)
#include "stm32f3xx.h"
#define FSDEV_PMA_SIZE (512u)
+ #define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *B, and *C: 1 x 16 bits/word
// PMA dedicated to USB (no sharing with CAN)
@@ -64,6 +67,7 @@
defined(STM32F303xD) || defined(STM32F303xE)
#include "stm32f3xx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
// NO internal Pull-ups
// *6, *8, *D, and *E: 2 x 16 bits/word LPM Support
// When CAN clock is enabled, USB can use first 768 bytes ONLY.
@@ -71,18 +75,22 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
#include "stm32l0xx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
#include "stm32l1xx.h"
#define FSDEV_PMA_SIZE (512u)
+ #define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
#include "stm32g4xx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
#include "stm32g0xx.h"
#define FSDEV_PMA_SIZE (2048u)
+ #define FSDEV_HAS_SBUF_ISO 1
#define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_EP_VTRX
@@ -107,6 +115,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
#include "stm32c0xx.h"
#define FSDEV_PMA_SIZE (2048u)
+ #define FSDEV_HAS_SBUF_ISO 1
#define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_CHEP_VTRX
#define USB_EP_CTR_TX USB_CHEP_VTTX
@@ -121,6 +130,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
#include "stm32h5xx.h"
#define FSDEV_PMA_SIZE (2048u)
+ #define FSDEV_HAS_SBUF_ISO 1
#define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_EP_VTRX
@@ -145,16 +155,19 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
#include "stm32wbxx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
/* ST provided header has incorrect value of USB_PMAADDR */
#define FSDEV_PMA_BASE USB1_PMAADDR
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
#include "stm32l4xx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
#include "stm32l5xx.h"
#define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_HAS_SBUF_ISO 0
#ifndef USB_PMAADDR
#define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
@@ -163,6 +176,7 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
#include "stm32u5xx.h"
#define FSDEV_PMA_SIZE (2048u)
+ #define FSDEV_HAS_SBUF_ISO 1
#define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_EP_VTRX
@@ -186,7 +200,36 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
#include "stm32u0xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_BUS_32BIT
+ // Disable SBUF_ISO on U0 for now due to bad performance (audio glitching)
+ #define FSDEV_HAS_SBUF_ISO 0
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
+ #include "stm32u3xx.h"
#define FSDEV_PMA_SIZE (2048u)
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_HAS_SBUF_ISO 1 // This is assumed to work but has not been tested...
#define USB USB_DRD_FS
#define USB_EP_CTR_RX USB_EP_VTRX
@@ -248,6 +291,36 @@
#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
+#ifndef FSDEV_HAS_SBUF_ISO
+ #error "FSDEV_HAS_SBUF_ISO not defined"
+#endif
+
+#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP
+ // Default configuration for double-buffered isochronous endpoints:
+ // - Enable double buffering on devices with >1KB Packet Memory Area (PMA)
+ // to improve isochronous transfer reliability and performance
+ // - Disable on devices with limited PMA to conserve memory space
+ #if FSDEV_PMA_SIZE > 1024u
+ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1
+ #else
+ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0
+ #endif
+#endif
+
+#if FSDEV_HAS_SBUF_ISO != 0 && CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP == 0
+ // SBUF_ISO configuration:
+ // - Some STM32 devices have special hardware support for single-buffered isochronous endpoints
+ // - When SBUF_ISO bit is available and double buffering is disabled:
+ // Enable SBUF_ISO to optimize endpoint register usage (one half of endpoint pair register)
+ #define FSDEV_USE_SBUF_ISO 1
+#else
+ // When either:
+ // - Hardware doesn't support SBUF_ISO feature, or
+ // - Double buffering is enabled for isochronous endpoints
+ // We must use the entire endpoint pair register
+ #define FSDEV_USE_SBUF_ISO 0
+#endif
+
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -291,6 +364,8 @@ static const IRQn_Type fsdev_irq[] = {
USB_IRQn,
#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
USB_DRD_FS_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32U3
+ USB_FS_IRQn,
#else
#error Unknown arch in USB driver
#endif
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index 21f13b279..8e6dc8c63 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -99,22 +99,12 @@ static void usb_phy_write(int addr, int data, int len)
}
}
-static void delay_ms(uint32_t ms)
-{
-#if CFG_TUSB_OS == OPT_OS_NONE
- int now = board_millis();
- while (board_millis() - now <= ms) asm("nop");
-#else
- osal_task_delay(ms);
-#endif
-}
-
static void USBC_HardwareReset(void)
{
// Reset phy and controller
USBC_REG_set_bit_l(USBPHY_CLK_RST_BIT, USBPHY_CLK_REG);
USBC_REG_set_bit_l(BUS_RST_USB_BIT, BUS_CLK_RST_REG);
- delay_ms(2);
+ tusb_time_delay_ms_api(2);
USBC_REG_set_bit_l(USBPHY_CLK_GAT_BIT, USBPHY_CLK_REG);
USBC_REG_set_bit_l(USBPHY_CLK_RST_BIT, USBPHY_CLK_REG);
@@ -186,7 +176,7 @@ static void USBC_ForceVbusValidToHigh(void)
USBC_Writel(reg_val, USBC_REG_ISCR(USBC0_BASE));
}
-void USBC_SelectBus(u32 io_type, u32 ep_type, u32 ep_index)
+static void USBC_SelectBus(u32 io_type, u32 ep_type, u32 ep_index)
{
u32 reg_val = 0;
@@ -962,7 +952,7 @@ void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
USBC_REG_set_bit_b(USBC_BP_POWER_D_RESUME, USBC_REG_PCTL(USBC0_BASE));
- delay_ms(10);
+ tusb_time_delay_ms_api(10);
USBC_REG_clear_bit_b(USBC_BP_POWER_D_RESUME, USBC_REG_PCTL(USBC0_BASE));
}
@@ -1092,6 +1082,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
musb_int_unmask();
}
+ #if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+ #endif
+
// 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)
{
@@ -1220,5 +1225,4 @@ void dcd_int_handler(uint8_t rhport)
rxis &= ~TU_BIT(num);
}
}
-
#endif
diff --git a/src/portable/sunxi/musb_def.h b/src/portable/sunxi/musb_def.h
index 53da5ded2..ce9b89d55 100644
--- a/src/portable/sunxi/musb_def.h
+++ b/src/portable/sunxi/musb_def.h
@@ -26,8 +26,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MUSB_DEF
-#define _TUSB_MUSB_DEF
+#ifndef TUSB_MUSB_DEF
+#define TUSB_MUSB_DEF
#define USBC_Readb(reg) (*(volatile unsigned char *)(reg))
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 52d675611..06579fbb3 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -39,6 +39,7 @@
#define DWC2_DEBUG 2
#include "device/dcd.h"
+#include "device/usbd_pvt.h"
#include "dwc2_common.h"
//--------------------------------------------------------------------+
@@ -52,6 +53,7 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
+// This variable is modified from ISR context, so it must be protected by critical section
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
@@ -75,6 +77,7 @@ CFG_TUD_MEM_SECTION static struct {
TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc2) {
#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ (void) dwc2;
return DWC2_EP_MAX;
#else
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
@@ -82,6 +85,12 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc
#endif
}
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_enabled(dwc2_dep_t* dep) {
+ return (dep->ctl & EPCTL_EPENA) != 0;
+}
//--------------------------------------------------------------------
// DMA
@@ -114,7 +123,7 @@ static void dma_setup_prepare(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) {
- if(dwc2->epout[0].doepctl & DOEPCTL_EPENA) {
+ if(edpt_is_enabled(&dwc2->epout[0])) {
return;
}
}
@@ -166,7 +175,7 @@ static void dma_setup_prepare(uint8_t rhport) {
- All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula):
- 13 for setup packets + control words (up to 3 setup packets).
- 1 for global NAK (not required/used here).
- - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
+ - Largest-EPsize/4 + 1. (FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
- 2 for each used OUT endpoint
Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
@@ -197,7 +206,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
}
} else {
// Check IN endpoints concurrently active limit
- if(dwc2_controller->ep_in_count) {
+ if(0 != dwc2_controller->ep_in_count) {
TU_ASSERT(_dcd_data.allocated_epin_count < dwc2_controller->ep_in_count);
_dcd_data.allocated_epin_count++;
}
@@ -214,10 +223,10 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
// Both TXFD and TXSA are in unit of 32-bit words.
if (epnum == 0) {
- dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top;
+ dwc2->dieptxf0 = ((uint32_t) fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top;
} else {
// DIEPTXF starts at FIFO #1.
- dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top;
+ dwc2->dieptxf[epnum - 1] = ((uint32_t) fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top;
}
}
@@ -235,10 +244,10 @@ static void dfifo_device_init(uint8_t rhport) {
if (is_dma) {
_dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count;
}
- dwc2->gdfifocfg = (_dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top;
+ dwc2->gdfifocfg = ((uint32_t) _dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top;
// Allocate FIFO for EP0 IN
- dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE);
+ (void) dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE);
}
@@ -279,17 +288,18 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
const uint8_t dir = tu_edpt_dir(ep_addr);
dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
+ const uint32_t stall_mask = (stall ? EPCTL_STALL : 0);
+
if (dir == TUSB_DIR_IN) {
- // Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(dep->diepctl & DIEPCTL_EPENA)) {
- dep->diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
+ if (!edpt_is_enabled(dep)) {
+ dep->diepctl |= DIEPCTL_SNAK | stall_mask;
} else {
// Stop transmitting packets and NAK IN xfers.
dep->diepctl |= DIEPCTL_SNAK;
while ((dep->diepint & DIEPINT_INEPNE) == 0) {}
// Disable the endpoint.
- dep->diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
+ dep->diepctl |= DIEPCTL_EPDIS | stall_mask;
while ((dep->diepint & DIEPINT_EPDISD_Msk) == 0) {}
dep->diepint = DIEPINT_EPDISD;
@@ -298,9 +308,10 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
// Flush the FIFO, and wait until we have confirmed it cleared.
dfifo_flush_tx(dwc2, epnum);
} else {
- // Only disable currently enabled non-control endpoint
- if ((epnum == 0) || !(dep->doepctl & DOEPCTL_EPENA)) {
- dep->doepctl |= stall ? DOEPCTL_STALL : 0;
+ if (!edpt_is_enabled(dep) || epnum == 0) {
+ // non-control not-enabled: stall if set
+ // For EP0 Out, keep it enabled to receive SETUP packets
+ dep->doepctl |= stall_mask;
} else {
// Asserting GONAK is required to STALL an OUT endpoint.
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
@@ -310,7 +321,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
// Ditto here disable the endpoint.
- dep->doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
+ dep->doepctl |= DOEPCTL_EPDIS | stall_mask;
while ((dep->doepint & DOEPINT_EPDISD_Msk) == 0) {}
dep->doepint = DOEPINT_EPDISD;
@@ -321,6 +332,9 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
+// Since this function returns void, it is not possible to return a boolean success message
+// We must make sure that this function is not called when the EP is disabled
+// Must be called from critical section
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
@@ -355,7 +369,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
const uint32_t odd_now = dsts.frame_number & 1u;
- if (odd_now) {
+ if (odd_now != 0) {
depctl.set_data0_iso_even = 1;
} else {
depctl.set_data1_iso_odd = 1;
@@ -374,7 +388,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
// Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
if (dir == TUSB_DIR_IN && total_bytes != 0) {
- dwc2->diepempmsk |= (1 << epnum);
+ dwc2->diepempmsk |= (1u << epnum); //-V629
}
}
}
@@ -397,7 +411,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Set device max speed
uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
if (is_highspeed) {
- dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
+ // dcfg Highspeed's mask is 0
// XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
// when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
@@ -420,8 +434,13 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
+#if CFG_TUSB_MCU == OPT_MCU_STM32N6
+ // No hardware detection of Vbus B-session is available on the STM32N6
+ dwc2->stm32_gccfg |= STM32_GCCFG_VBVALOVAL;
+#endif
+
// Enable required interrupts
- dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
// TX FIFO empty level for interrupt is complete empty
uint32_t gahbcfg = dwc2->gahbcfg;
@@ -531,6 +550,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+ usbd_spin_lock(false);
+
_dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
@@ -539,7 +560,7 @@ void dcd_edpt_close_all(uint8_t rhport) {
for (uint8_t n = 1; n < ep_count; n++) {
for (uint8_t d = 0; d < 2; d++) {
dwc2_dep_t* dep = &dwc2->ep[d][n];
- if (dep->ctl & EPCTL_EPENA) {
+ if (edpt_is_enabled(dep)) {
dep->ctl |= EPCTL_SNAK | EPCTL_EPDIS;
}
xfer_status[n][1-d].max_size = 0;
@@ -548,8 +569,9 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
-
dfifo_device_init(rhport); // re-init dfifo
+
+ usbd_spin_unlock(false);
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -567,21 +589,31 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpo
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
-
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
+ bool ret;
- // EP0 can only handle one packet
- if (epnum == 0) {
- _dcd_data.ep0_pending[dir] = total_bytes;
+ usbd_spin_lock(false);
+
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
+
+ // EP0 can only handle one packet
+ if (epnum == 0) {
+ _dcd_data.ep0_pending[dir] = total_bytes;
+ }
+
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
}
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_unlock(false);
- return true;
+ return ret;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
@@ -594,24 +626,38 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
-
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
+ bool ret;
- // Schedule packets to be sent within interrupt
- // TODO xfer fifo may only available for slave mode
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_lock(false);
- return true;
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+
+ // Schedule packets to be sent within interrupt
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
+ }
+
+ usbd_spin_unlock(false);
+
+ return ret;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
edpt_disable(rhport, ep_addr, true);
- if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) {
- dma_setup_prepare(rhport);
+
+ // For control endpoint, prepare to receive SETUP packet
+ if (tu_edpt_number(ep_addr) == 0) {
+ if (dma_device_enabled(dwc2)) {
+ dma_setup_prepare(rhport);
+ }
}
}
@@ -631,6 +677,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
//--------------------------------------------------------------------
// 7.4.1 Initialization on USB Reset
+// Must be called from critical section
static void handle_bus_reset(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
const uint8_t ep_count = dwc2_ep_count(dwc2);
@@ -647,8 +694,9 @@ static void handle_bus_reset(uint8_t rhport) {
// Disable all IN endpoints
for (uint8_t n = 0; n < ep_count; n++) {
- if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
- dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+ dwc2_dep_t* dep = &dwc2->epin[n];
+ if (edpt_is_enabled(dep)) {
+ dep->diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
}
}
@@ -667,12 +715,23 @@ static void handle_bus_reset(uint8_t rhport) {
dcfg.address = 0;
dwc2->dcfg = dcfg.value;
- // Fixed both control EP0 size to 64 bytes
- dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
- dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
+ // 6. Configure maximum packet size for EP0
+ uint8_t mps = 0;
+ switch (CFG_TUD_ENDPOINT0_SIZE) {
+ case 8: mps = 3; break;
+ case 16: mps = 2; break;
+ case 32: mps = 1; break;
+ case 64: mps = 0; break;
+ default: mps = 0; break;
+ }
+
+ dwc2->epin[0].ctl &= ~DIEPCTL0_MPSIZ_Msk;
+ dwc2->epout[0].ctl &= ~DOEPCTL0_MPSIZ_Msk;
+ dwc2->epin[0].ctl |= mps << DIEPCTL0_MPSIZ_Pos;
+ dwc2->epout[0].ctl |= mps << DOEPCTL0_MPSIZ_Pos;
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN].max_size = 64;
+ xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
if(dma_device_enabled(dwc2)) {
dma_setup_prepare(rhport);
@@ -762,25 +821,26 @@ static void handle_rxflvl_irq(uint8_t rhport) {
const uint16_t byte_count = grxstsp.byte_count;
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- if (byte_count) {
+ if (byte_count != 0) {
// Read packet off RxFIFO
- if (xfer->ff) {
+ if (xfer->ff != NULL) {
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
} else {
dfifo_read_packet(dwc2, xfer->buffer, byte_count);
xfer->buffer += byte_count;
}
+ }
- // short packet, minus remaining bytes (xfer_size)
- if (byte_count < xfer->max_size) {
- const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
- xfer->total_len -= tsiz.xfer_size;
- if (epnum == 0) {
- xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
- _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
- }
+ // short packet (including ZLP when byte_count == 0), minus remaining bytes (xfer_size)
+ if (byte_count < xfer->max_size) {
+ const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
+ xfer->total_len -= tsiz.xfer_size;
+ if (epnum == 0) {
+ xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
+ _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
}
}
+
break;
}
@@ -790,12 +850,17 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// the specified OUT endpoint which will be handled by handle_epout_irq()
break;
- default: break;
+ default: break; // nothing to do
}
}
static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
if (doepint_bm.setup_phase_done) {
+ // Cleanup previous pending EP0 IN transfer if any
+ dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
+ if (edpt_is_enabled(epin0)) {
+ edpt_disable(rhport, 0x80, false);
+ }
dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
return;
}
@@ -807,7 +872,6 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe
// can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
-
if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
// EP0 can only handle one packet, Schedule another packet to be received.
edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
@@ -824,7 +888,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
if (diepint_bm.xfer_complete) {
- if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ if ((epnum == 0) && (0 != _dcd_data.ep0_pending[TUSB_DIR_IN])) {
// EP0 can only handle one packet. Schedule another packet to be transmitted.
edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
} else {
@@ -835,7 +899,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
// TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than
// - 64 bytes or
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
- if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) {
+ if (diepint_bm.txfifo_empty && tu_bit_test(dwc2->diepempmsk, epnum)) {
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
const uint16_t remain_packets = tsiz.packet_count;
@@ -851,7 +915,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
}
// Push packet to Tx-FIFO
- if (xfer->ff) {
+ if (xfer->ff != NULL) {
volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
} else {
@@ -863,7 +927,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
// Turn off TXFE if all bytes are written.
tsiz.value = epin->tsiz;
if (tsiz.xfer_size == 0) {
- dwc2->diepempmsk &= ~(1 << epnum);
+ dwc2->diepempmsk &= ~(1u << epnum);
}
}
}
@@ -874,6 +938,11 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
if (doepint_bm.setup_phase_done) {
+ // Cleanup previous pending EP0 IN transfer if any
+ dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
+ if (edpt_is_enabled(epin0)) {
+ edpt_disable(rhport, 0x80, false);
+ }
dma_setup_prepare(rhport);
dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
@@ -937,7 +1006,7 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
// DAINT for a given EP clears when DEPINTx is cleared.
// EPINT will be cleared when DAINT bits are cleared.
for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
- if (dwc2->daint & TU_BIT(daint_offset + epnum)) {
+ if (tu_bit_test(dwc2->daint,daint_offset + epnum)) {
dwc2_dep_t* epout = &ep_base[epnum];
union {
uint32_t value;
@@ -946,7 +1015,7 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
} intr;
intr.value = epout->intr;
- epout->intr = intr.value; // Clear interrupt
+ epout->intr = intr.value; // Clear interrupt //-V::2584::{otg_int}
if (is_dma) {
#if CFG_TUD_DWC2_DMA_ENABLE
@@ -983,29 +1052,36 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
*/
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
const uint32_t gintmask = dwc2->gintmsk;
const uint32_t gintsts = dwc2->gintsts & gintmask;
if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
+
+ usbd_spin_lock(true);
handle_bus_reset(rhport);
+ usbd_spin_unlock(true);
}
if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
dwc2->gintsts = GINTSTS_ENUMDNE;
+ // There may be a pending suspend event, so we clear it first
+ dwc2->gintsts = GINTSTS_USBSUSP;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
handle_enum_done(rhport);
}
if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
+ dwc2->gintmsk &= ~GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1025,6 +1101,7 @@ void dcd_int_handler(uint8_t rhport) {
if(gintsts & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
diff --git a/src/portable/synopsys/dwc2/dwc2_at32.h b/src/portable/synopsys/dwc2/dwc2_at32.h
new file mode 100644
index 000000000..513495eb1
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_at32.h
@@ -0,0 +1,122 @@
+/*
+ * 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 DWC2_AT32_H_
+#define DWC2_AT32_H_
+
+#define DWC2_EP_MAX TUP_DCD_ENDPOINT_MAX
+
+#if CFG_TUSB_MCU == OPT_MCU_AT32F415
+ #include <at32f415.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F435_437
+ #include <at32f435_437.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG2_FIFO_SIZE 1280
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define OTG2_IRQn OTGFS2_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+ #define DWC2_OTG2_REG_BASE 0x40040000UL
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F423
+ #include <at32f423.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F402_405
+ #include <at32f402_405.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG2_FIFO_SIZE 4096
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define OTG2_IRQn OTGHS_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+ #define DWC2_OTG2_REG_BASE 0x40040000UL //OTGHS
+#elif CFG_TUSB_MCU == OPT_MCU_AT32F425
+ #include <at32f425.h>
+ #define OTG1_FIFO_SIZE 1280
+ #define OTG1_IRQn OTGFS1_IRQn
+ #define DWC2_OTG1_REG_BASE 0x50000000UL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const dwc2_controller_t _dwc2_controller[] = {
+ {.reg_base = DWC2_OTG1_REG_BASE, .irqnum = OTG1_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = OTG1_FIFO_SIZE},
+#if defined DWC2_OTG2_REG_BASE
+ {.reg_base = DWC2_OTG2_REG_BASE, .irqnum = OTG2_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = OTG2_FIFO_SIZE}
+#endif
+};
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ (void) role;
+ const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum;
+ if (enabled) {
+ NVIC_EnableIRQ(irqn);
+ } else {
+ NVIC_DisableIRQ(irqn);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) {
+ NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
+ // try to delay for 1 ms
+ uint32_t count = system_core_clock / 1000;
+ while (count--) __asm volatile("nop");
+}
+
+// MCU specific PHY init, called BEFORE core reset
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ // Enable on-chip HS PHY
+ if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
+ } else if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
+ }
+}
+
+// MCU specific PHY update, it is called AFTER init() and core reset
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t *dwc2, uint8_t hs_phy_type) {
+ (void) dwc2;
+ (void) hs_phy_type;
+
+ dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN | STM32_GCCFG_DCDEN | STM32_GCCFG_PDEN;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DWC2_AT32_H_ */
diff --git a/src/portable/synopsys/dwc2/dwc2_bcm.h b/src/portable/synopsys/dwc2/dwc2_bcm.h
index e5824606a..df6d4a852 100644
--- a/src/portable/synopsys/dwc2/dwc2_bcm.h
+++ b/src/portable/synopsys/dwc2/dwc2_bcm.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DWC2_BCM_H_
-#define _TUSB_DWC2_BCM_H_
+#ifndef TUSB_DWC2_BCM_H_
+#define TUSB_DWC2_BCM_H_
#ifdef __cplusplus
extern "C" {
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
index e1e7d5c1a..980574e12 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.c
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ * Copyright (c) 2024-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
@@ -29,33 +29,32 @@
#define DWC2_COMMON_DEBUG 2
#if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
-
-#if CFG_TUD_ENABLED
-#include "device/dcd.h"
-#endif
-
-#if CFG_TUH_ENABLED
-#include "host/hcd.h"
-#include "host/usbh.h"
-#endif
-
#include "dwc2_common.h"
//--------------------------------------------------------------------
//
//--------------------------------------------------------------------
static void reset_core(dwc2_regs_t* dwc2) {
+ // The software must check that bit 31 in this register is set to 1 (AHB Master is Idle) before starting any operation
+ while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {
+ }
+
+ // load gsnpsid (it is not readable after reset is asserted)
+ const uint32_t gsnpsid = dwc2->gsnpsid;
+
// reset core
dwc2->grstctl |= GRSTCTL_CSRST;
- if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
- // prior v42.0 CSRST is self-clearing
+ if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ // prior v4.20a: CSRST is self-clearing and the core clears this bit after all the necessary logic is reset in
+ // the core, which can take several clocks, depending on the current state of the core. Once this bit has been
+ // cleared, the software must wait at least 3 PHY clocks before accessing the PHY domain (synchronization delay).
while (dwc2->grstctl & GRSTCTL_CSRST) {}
} else {
- // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking.
- // CSRST must also be explicitly cleared
+ // From v4.20a: CSRST bit is write only. The application must clear this bit after checking the bit 29 of this
+ // register i.e Core Soft Reset Done CSRT_DONE (w1c)
while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {}
- dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE;
+ dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE;
}
while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
@@ -92,6 +91,14 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4};
+ uint8_t phy_width;
+ if (CFG_TUSB_MCU != OPT_MCU_AT32F402_405 && // at32f402_405 does not support 16-bit
+ ghwcfg4.phy_data_width) {
+ phy_width = 16; // 16-bit PHY interface if supported
+ } else {
+ phy_width = 8; // 8-bit PHY interface
+ }
+
// De-select FS PHY
gusbcfg &= ~GUSBCFG_PHYSEL;
@@ -119,10 +126,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
// Set 16-bit interface if supported
- if (ghwcfg4.phy_data_width) {
- gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ if (phy_width == 16) {
+ gusbcfg |= GUSBCFG_PHYIF16;
} else {
- gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
+ gusbcfg &= ~GUSBCFG_PHYIF16;
}
}
@@ -139,7 +146,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// - 9 if using 8-bit PHY interface
// - 5 if using 16-bit PHY interface
gusbcfg &= ~GUSBCFG_TRDT_Msk;
- gusbcfg |= (ghwcfg4.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
+ gusbcfg |= (phy_width == 16 ? 5u : 9u) << GUSBCFG_TRDT_Pos;
dwc2->gusbcfg = gusbcfg;
// MCU specific PHY update post reset
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index 18b93894f..dc204f578 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -30,6 +30,14 @@
#include "common/tusb_common.h"
#include "dwc2_type.h"
+#if CFG_TUD_ENABLED
+#include "device/dcd.h"
+#endif
+
+#if CFG_TUH_ENABLED
+#include "host/hcd.h"
+#endif
+
// Following symbols must be defined by port header
// - _dwc2_controller[]: array of controllers
// - DWC2_EP_MAX: largest EP counts of all controllers
@@ -49,6 +57,10 @@
#include "dwc2_efm32.h"
#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
#include "dwc2_xmc.h"
+#elif defined(TUP_USBIP_DWC2_AT32)
+ #include "dwc2_at32.h"
+#elif defined(TUP_USBIP_DWC2_NRF)
+ #include "dwc2_nrf.h"
#else
#error "Unsupported MCUs"
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 3309760ff..a4e0d1770 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -47,6 +47,23 @@ static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 }
};
+#elif TU_CHECK_MCU(OPT_MCU_ESP32H4)
+// H4's USB_WRAP register block uses "wrap_*" field names. Map them to the
+// names used by TinyUSB's DWC2 port to keep the source unchanged.
+#define otg_conf wrap_otg_conf
+#define pad_pull_override wrap_pad_pull_override
+#define dp_pullup wrap_dp_pullup
+#define dp_pulldown wrap_dp_pulldown
+#define dm_pullup wrap_dm_pullup
+#define dm_pulldown wrap_dm_pulldown
+
+#define DWC2_FS_REG_BASE 0x60040000UL
+#define DWC2_EP_MAX 7
+
+static const dwc2_controller_t _dwc2_controller[] = {
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 }
+};
+
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
#define DWC2_FS_REG_BASE 0x50040000UL
#define DWC2_HS_REG_BASE 0x50000000UL
@@ -55,8 +72,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
-{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
-{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
+ { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
};
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index dec021f59..f655e4dba 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5 HS | XMC4500 | GD32VF103 |
-|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:-------------|:-------------|:------------|
-| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
-| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
-| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
-| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
-| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
-| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
-| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
-| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
-| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
-| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | nRF54 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------|
+| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54430A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 4.30a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0xAA555000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x228BFC72 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
+| - single_point | hub | hub | hub | hub | hub | n/a | hub | n/a | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | UTMI+ | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | n/a | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
+| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x0BEAC0E8 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
+| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
+| - optional_feature_removed | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 3050 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x1E10AA60 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
+| - num_dev_period_in_ep | 0 | 0 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - ahb_freq_min | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
+| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - vbus_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index 25edcf22d..f6bd2785a 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -9,10 +9,14 @@ import pandas as pd
# Note: FS is FullSpeed, HS is HighSpeed
dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4']
dwc2_reg_value = {
+ 'AT32 F405 FS': [0x00002000, 0x4F54400A, 0x00000000, 0x228FDD00, 0x020004E8, 0x1FF0A020],
+ 'AT32 F405 HS': [0x00000000, 0x4F54400A, 0x00000000, 0x229FDDD0, 0x03F006E8, 0x1FF0A020],
+ 'AT32 F415': [0x00001000, 0x4F54400A, 0x00000000, 0x228DCD00, 0x020004E8, 0x0F],
'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020],
'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030],
'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030],
'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
+ 'nRF54': [0, 0x4F54430A, 0xAA555000, 0x228BFC72, 0x0BEAC0E8, 0x1E10AA60],
'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030],
'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030],
'ST F412/76x FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
@@ -21,7 +25,7 @@ dwc2_reg_value = {
'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
- 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ 'ST U5A5/H7RS/N6 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
}
diff --git a/src/portable/synopsys/dwc2/dwc2_nrf.h b/src/portable/synopsys/dwc2/dwc2_nrf.h
new file mode 100644
index 000000000..b93571f16
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_nrf.h
@@ -0,0 +1,61 @@
+/*
+ * 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_DWC2_NRF_H
+#define TUSB_DWC2_NRF_H
+
+#include "nrf.h"
+
+#define DWC2_EP_MAX 16
+
+static const dwc2_controller_t _dwc2_controller[] = {
+ { .reg_base = NRF_USBHSCORE0_NS_BASE, .irqnum = USBHS_IRQn, .ep_count = 16, .ep_fifo_size = 12288 },
+};
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ (void) rhport;
+ (void) role;
+ (void) enabled;
+}
+
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
+}
+
+// MCU specific PHY init, called BEFORE core reset
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+}
+
+// MCU specific PHY update, it is called AFTER init() and core reset
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index c11c1eb05..9da8de41f 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -77,6 +77,17 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
+#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
+ #include "stm32n6xx.h"
+ #define EP_MAX_FS 9
+ #define EP_FIFO_SIZE_FS 4096
+
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+
+ #define USB_OTG_HS_PERIPH_BASE USB1_OTG_HS_BASE
+ #define OTG_HS_IRQn USB1_OTG_HS_IRQn
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
@@ -102,6 +113,22 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
#endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32WBA
+ #if defined(STM32WBA62xx)
+ #include "stm32wba62xx.h"
+ #elif defined(STM32WBA64xx)
+ #include "stm32wba64xx.h"
+ #elif defined(STM32WBA65xx)
+ #include "stm32wba65xx.h"
+ #else
+ #error "The selected STM32WBA series chip does not support OTG USB HS"
+ #endif
+
+ #define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE_NS
+ #define OTG_HS_IRQn USB_OTG_HS_IRQn
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
#else
#error "Unsupported MCUs"
#endif
@@ -149,12 +176,15 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_
TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// try to delay for 1 ms
uint32_t count = SystemCoreClock / 1000;
- while (count--) __NOP();
+ while (count--) {
+ __NOP();
+ }
}
// MCU specific PHY init, called BEFORE core reset
// - dwc2 3.30a (H5) use USB_HS_PHYC
// - dwc2 4.11a (U5) use femtoPHY
+// - dwc2 x.xxx (WBA) use USB_OTG_HS
static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Enable on-chip FS PHY
@@ -183,11 +213,10 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
#endif
} else {
-#if CFG_TUSB_MCU != OPT_MCU_STM32U5
+#if CFG_TUSB_MCU != OPT_MCU_STM32U5 && CFG_TUSB_MCU != OPT_MCU_STM32WBA
// Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable'
dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN;
#endif
-
// Enable on-chip HS PHY
if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
#ifdef USB_HS_PHYC
@@ -222,6 +251,9 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// Enable PLL internal PHY
USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN;
+
+ // Wait ~2ms until the PLL is ready (there's no RDY bit to query)
+ tusb_time_delay_ms_api(2);
#else
#endif
@@ -268,6 +300,77 @@ static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
}
}
+//------------- DCache -------------//
+#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
+
+typedef struct {
+ uintptr_t start;
+ uintptr_t end;
+} mem_region_t;
+
+// Can be used to define additional uncached regions
+#ifndef CFG_DWC2_MEM_UNCACHED_REGIONS
+#define CFG_DWC2_MEM_UNCACHED_REGIONS
+#endif
+
+static mem_region_t uncached_regions[] = {
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+#if CFG_TUSB_MCU == OPT_MCU_STM32H7
+ {.start = 0x20000000, .end = 0x2001FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+ {.start = 0x20000000, .end = 0x2002FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
+ // DTCM
+ {.start = 0x20000000, .end = 0x2000FFFF},
+#else
+#error "Cache maintenance is not supported yet"
+#endif
+ CFG_DWC2_MEM_UNCACHED_REGIONS
+};
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) {
+ size = (size & ~(CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) + CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT;
+ }
+ return size;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uintptr_t addr) {
+ for (unsigned int i = 0; i < TU_ARRAY_SIZE(uncached_regions); i++) {
+ if (uncached_regions[i].start <= addr && addr <= uncached_regions[i].end) { return false; }
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(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 dwc2_dcache_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(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 dwc2_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index 0a8dacf5f..adcc579e3 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -1435,7 +1435,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000
#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits
-#define DAINT_SHIFT(_dir) ((_dir == TUSB_DIR_IN) ? 0 : 16)
+#define DAINT_SHIFT(_dir) (((_dir) == TUSB_DIR_IN) ? 0 : 16)
#if 0
/******************** Bit definition for OTG register ********************/
@@ -1847,6 +1847,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define HPTXFSIZ_PTXFD HPTXFSIZ_PTXFD_Msk // Host periodic TxFIFO depth
/******************** Bit definition for DIEPCTL register ********************/
+#define DIEPCTL0_MPSIZ_Pos (0U)
+#define DIEPCTL0_MPSIZ_Msk (0x3UL << DIEPCTL0_MPSIZ_Pos) // 0x00000003
+#define DIEPCTL0_MPSIZ DIEPCTL0_MPSIZ_Msk // Maximum packet size(endpoint 0)
#define DIEPCTL_MPSIZ_Pos (0U)
#define DIEPCTL_MPSIZ_Msk (0x7FFUL << DIEPCTL_MPSIZ_Pos) // 0x000007FF
#define DIEPCTL_MPSIZ DIEPCTL_MPSIZ_Msk // Maximum packet size
@@ -2155,6 +2158,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define EPCTL_EPENA EPCTL_EPENA_Msk // Endpoint enable
/******************** Bit definition for DOEPCTL register ********************/
+#define DOEPCTL0_MPSIZ_Pos (0U)
+#define DOEPCTL0_MPSIZ_Msk (0x3UL << DOEPCTL0_MPSIZ_Pos) // 0x00000003
+#define DOEPCTL0_MPSIZ DOEPCTL0_MPSIZ_Msk // Maximum packet size(endpoint 0)
#define DOEPCTL_MPSIZ_Pos (0U)
#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF
#define DOEPCTL_MPSIZ DOEPCTL_MPSIZ_Msk // Maximum packet size //Bit 1
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index 7c090f427..2b2bfc91a 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -382,6 +382,10 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// force host mode and wait for mode switch
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
+#if CFG_TUSB_MCU == OPT_MCU_STM32N6
+ // No hardware detection of Vbus B-session is available on the STM32N6
+ dwc2->stm32_gccfg &= ~STM32_GCCFG_VBVALOVAL;
+#endif
while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
// configure fixed-allocated fifo scheme
@@ -1157,7 +1161,6 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
- const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
bool is_done = false;
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index 3752ae251..e4c28a56f 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -333,11 +333,20 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport; (void) ep_addr;
- // TODO implement dcd_edpt_close()
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
}
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
+}
+
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
@@ -662,24 +671,10 @@ static void handle_setup_packet(void)
dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
}
-#if CFG_TUSB_OS == OPT_OS_NONE
-TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) {
- // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles
- // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop
- volatile uint32_t cycles = (25000 * ms) >> 2;
- while (cycles > 0) {
- cycles--;
- asm("nop");
- }
-}
-#else
-#define tu_delay(ms) osal_task_delay(ms)
-#endif
-
static void handle_bus_power_event(void *param) {
(void) param;
- tu_delay(5); // Bus power settling delay.
+ tusb_time_delay_ms_api(5); // Bus power settling delay.
USBKEYPID = USBKEY;
@@ -694,7 +689,7 @@ static void handle_bus_power_event(void *param) {
uint16_t attempts = 0;
do { // Poll the PLL, checking for a successful lock.
USBPLLIR = 0;
- tu_delay(1);
+ tusb_time_delay_ms_api(1);
attempts++;
} while ((attempts < 10) && (USBPLLIR != 0));
@@ -835,5 +830,4 @@ void dcd_int_handler(uint8_t rhport)
}
}
-
#endif
diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c
index a03c94558..f0e52ae47 100644
--- a/src/portable/valentyusb/eptri/dcd_eptri.c
+++ b/src/portable/valentyusb/eptri/dcd_eptri.c
@@ -438,9 +438,17 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
- (void) rhport; (void) ep_addr;
- // TODO implement dcd_edpt_close()
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+ (void)ep_addr;
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
void dcd_edpt_close_all (uint8_t rhport)
@@ -659,5 +667,4 @@ void dcd_int_handler(uint8_t rhport)
}
}
}
-
#endif
diff --git a/src/portable/valentyusb/eptri/dcd_eptri.h b/src/portable/valentyusb/eptri/dcd_eptri.h
index d67635d7c..2fe74a712 100644
--- a/src/portable/valentyusb/eptri/dcd_eptri.h
+++ b/src/portable/valentyusb/eptri/dcd_eptri.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DCD_VALENTYUSB_EPTRI_H_
-#define _TUSB_DCD_VALENTYUSB_EPTRI_H_
+#ifndef TUSB_DCD_VALENTYUSB_EPTRI_H_
+#define TUSB_DCD_VALENTYUSB_EPTRI_H_
#include "common/tusb_common.h"
#ifdef __cplusplus
@@ -36,4 +36,4 @@
}
#endif
-#endif /* _TUSB_DCD_VALENTYUSB_EPTRI_H_ */
+#endif /* TUSB_DCD_VALENTYUSB_EPTRI_H_ */
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
index c248ba14e..f8c3b05c3 100644
--- a/src/portable/wch/dcd_ch32_usbfs.c
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -283,12 +283,20 @@ void dcd_edpt_close_all(uint8_t rhport) {
// TODO optional
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
(void) rhport;
(void) ep_addr;
- // TODO optional
+ (void)largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
+ (void)rhport;
+ (void)desc_ep;
+ return false;
}
+
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
(void) rhport;
uint8_t ep = tu_edpt_number(ep_addr);
@@ -344,5 +352,4 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
}
}
}
-
#endif
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index f8bf3c889..36fdc89ef 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -27,13 +27,14 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
-#include "ch32_usbhs_reg.h"
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && defined(CFG_TUD_WCH_USBIP_USBHS) && \
+ (CFG_TUD_WCH_USBIP_USBHS == 1)
+ #include "ch32_usbhs_reg.h"
-#include "device/dcd.h"
+ #include "device/dcd.h"
-// Max number of bi-directional endpoints including EP0
-#define EP_MAX 16
+ // Max number of bi-directional endpoints including EP0
+ #define EP_MAX 16
typedef struct {
uint8_t* buffer;
@@ -288,6 +289,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
}
}
+ #if 0
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false;
+}
+
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false;
+}
+ #endif
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
@@ -419,5 +435,4 @@ void dcd_int_handler(uint8_t rhport) {
USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
}
}
-
#endif
diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c
new file mode 100644
index 000000000..7bbf122dd
--- /dev/null
+++ b/src/portable/wch/hcd_ch32_usbfs.c
@@ -0,0 +1,679 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Mitsumine Suzu (verylowfreq)
+ *
+ * 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 && defined(TUP_USBIP_WCH_USBFS) && defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS
+
+#include <stdlib.h>
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "host/usbh_pvt.h"
+
+#include "bsp/board_api.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#include "ch32v20x.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include "ch32v20x_usb.h"
+
+#define USBFS_RX_BUF_LEN 64
+#define USBFS_TX_BUF_LEN 64
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_RX_Buf[USBFS_RX_BUF_LEN];
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_TX_Buf[USBFS_TX_BUF_LEN];
+
+#define USB_XFER_TIMEOUT_MILLIS 100
+// #define USB_INTERRUPT_XFER_TIMEOUT_MILLIS 1
+
+#define PANIC(...) \
+ do { \
+ printf("%s() L%d: ", __func__, __LINE__); \
+ printf("\r\n[PANIC] " __VA_ARGS__); \
+ while (true) {} \
+ } while (false)
+
+#define LOG_CH32_USBFSH(...) TU_LOG3(__VA_ARGS__)
+
+// Busywait for delay microseconds/nanoseconds
+TU_ATTR_ALWAYS_INLINE static inline void loopdelay(uint32_t count) {
+ volatile uint32_t c = count / 3;
+ if (c == 0) { return; }
+ // while (c-- != 0);
+ asm volatile(
+ "1: \n" // loop label
+ " addi %0, %0, -1 \n" // c--
+ " bne %0, zero, 1b \n" // if (c != 0) goto loop
+ : "+r"(c) // c is input/output operand
+ );
+}
+
+// Endpoint status
+typedef struct usb_edpt {
+ // Is this a valid struct
+ bool configured;
+
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t max_packet_size;
+
+ uint8_t xfer_type;
+
+ // Data toggle (0 or not 0) for DATA0/1
+ uint8_t data_toggle;
+
+ bool is_nak_pending;
+ uint16_t buflen;
+ uint8_t* buf;
+} usb_edpt_t;
+
+static usb_edpt_t usb_edpt_list[CFG_TUH_DEVICE_MAX * 6] = {};
+
+typedef struct usb_current_xfer_st {
+ bool is_busy;
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ // Xfer started time in millis for timeout
+ uint32_t start_ms;
+ uint8_t *buffer;
+ uint16_t bufferlen;
+ uint16_t xferred_len;
+ bool nak_pending;
+} usb_current_xfer_t;
+
+static volatile usb_current_xfer_t usb_current_xfer_info = {};
+
+static usb_edpt_t *get_edpt_record(uint8_t dev_addr, uint8_t ep_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ usb_edpt_t *cur = &usb_edpt_list[i];
+ if (cur->configured && cur->dev_addr == dev_addr && cur->ep_addr == ep_addr) {
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *get_empty_record_slot(void) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (!usb_edpt_list[i].configured) {
+ return &usb_edpt_list[i];
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *slot = get_empty_record_slot();
+ TU_ASSERT(slot != NULL, NULL);
+
+ slot->dev_addr = dev_addr;
+ slot->ep_addr = ep_addr;
+ slot->max_packet_size = max_packet_size;
+ slot->xfer_type = xfer_type;
+ slot->data_toggle = 0;
+ slot->is_nak_pending = false;
+ slot->buflen = 0;
+ slot->buf = NULL;
+
+ slot->configured = true;
+
+ return slot;
+}
+
+static usb_edpt_t *get_or_add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *ret = get_edpt_record(dev_addr, ep_addr);
+ if (ret != NULL) {
+ return ret;
+ } else {
+ return add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type);
+ }
+}
+
+static void remove_edpt_record_for_device(uint8_t dev_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (usb_edpt_list[i].configured && usb_edpt_list[i].dev_addr == dev_addr) {
+ usb_edpt_list[i].configured = false;
+ }
+ }
+}
+
+// static void dump_edpt_record_list() {
+// for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+// usb_edpt_t* cur = &usb_edpt_list[i];
+// if (cur->configured) {
+// printf("[%2d] Device 0x%02x Endpoint 0x%02x\r\n", i, cur->dev_addr, cur->ep_addr);
+// } else {
+// printf("[%2d] not configured\r\n", i);
+// }
+// }
+// }
+
+static bool interrupt_enabled = false;
+
+/** Enable or disable USBFS Host function */
+static void hardware_init_host(bool enabled) {
+ // Reset USBOTG module
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
+
+ tusb_time_delay_ms_api(1);
+ USBOTG_H_FS->BASE_CTRL = 0;
+
+ if (!enabled) {
+ // Disable all feature
+ USBOTG_H_FS->BASE_CTRL = 0;
+ } else {
+ // Enable USB Host features
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(0);
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_HOST_MODE | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
+ USBOTG_H_FS->HOST_EP_MOD = USBFS_UH_EP_TX_EN | USBFS_UH_EP_RX_EN;
+ USBOTG_H_FS->HOST_RX_DMA = (uint32_t) USBFS_RX_Buf;
+ USBOTG_H_FS->HOST_TX_DMA = (uint32_t) USBFS_TX_Buf;
+ // USBOTG_H_FS->INT_EN = USBFS_UIE_TRANSFER | USBFS_UIE_DETECT;
+ USBOTG_H_FS->INT_EN = USBFS_UIE_DETECT;
+ }
+}
+
+static bool hardware_start_xfer(uint8_t pid, uint8_t ep_addr, uint8_t data_toggle) {
+ LOG_CH32_USBFSH("hardware_start_xfer(pid=%s(0x%02x), ep_addr=0x%02x, toggle=%d)\r\n",
+ pid == USB_PID_IN ? "IN" : pid == USB_PID_OUT ? "OUT"
+ : pid == USB_PID_SETUP ? "SETUP"
+ : "(other)",
+ pid, ep_addr, data_toggle);
+
+ //WORKAROUND: For LowSpeed device, insert small delay
+ bool is_lowspeed_device = tuh_speed_get(usb_current_xfer_info.dev_addr) == TUSB_SPEED_LOW;
+ if (is_lowspeed_device) {
+ //NOTE: worked -> SystemCoreClock / 1000000 * 50, 25
+ // NOT worked -> 20 and less (at 144MHz internal clock)
+ loopdelay(SystemCoreClock / 1000000 * 40);
+ }
+
+ uint8_t pid_edpt = (pid << 4) | (tu_edpt_number(ep_addr) & 0x0f);
+ USBOTG_H_FS->HOST_TX_CTRL = (data_toggle != 0) ? USBFS_UH_T_TOG : 0;
+ USBOTG_H_FS->HOST_RX_CTRL = (data_toggle != 0) ? USBFS_UH_R_TOG : 0;
+ USBOTG_H_FS->HOST_EP_PID = pid_edpt;
+ USBOTG_H_FS->INT_EN |= USBFS_UIE_TRANSFER;
+ USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ return true;
+}
+
+
+/** Set device address to communicate */
+static void hardware_update_device_address(uint8_t dev_addr) {
+ // Keep the bit of GP_BIT. Other 7bits are actual device address.
+ USBOTG_H_FS->DEV_ADDR = (USBOTG_H_FS->DEV_ADDR & USBFS_UDA_GP_BIT) | (dev_addr & USBFS_USB_ADDR_MASK);
+}
+
+/** Set port speed */
+static void hardware_update_port_speed(tusb_speed_t speed) {
+ LOG_CH32_USBFSH("hardware_update_port_speed(%s)\r\n", speed == TUSB_SPEED_FULL ? "Full" : speed == TUSB_SPEED_LOW ? "Low"
+ : "(invalid)");
+ switch (speed) {
+ case TUSB_SPEED_LOW:
+ USBOTG_H_FS->BASE_CTRL |= USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_PRE_PID_EN;
+ return;
+ case TUSB_SPEED_FULL:
+ USBOTG_H_FS->BASE_CTRL &= ~USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP &= ~USBFS_UH_PRE_PID_EN;
+ return;
+ default:
+ PANIC("hardware_update_port_speed(%d)\r\n", speed);
+ }
+}
+
+static void hardware_set_port_address_speed(uint8_t dev_addr) {
+ hardware_update_device_address(dev_addr);
+ tusb_speed_t rhport_speed = hcd_port_speed_get(0);
+ tusb_speed_t dev_speed = tuh_speed_get(dev_addr);
+ hardware_update_port_speed(dev_speed);
+ if (rhport_speed == TUSB_SPEED_FULL && dev_speed == TUSB_SPEED_LOW) {
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ }
+}
+
+static bool hardware_device_attached(void) {
+ return USBOTG_H_FS->MIS_ST & USBFS_UMS_DEV_ATTACH;
+}
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void) rhport;
+ (void) rh_init;
+ hardware_init_host(true);
+
+ return true;
+}
+
+bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+ hardware_init_host(false);
+
+ return true;
+}
+
+static bool int_state_for_portreset = false;
+
+void hcd_port_reset(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset()\r\n");
+ int_state_for_portreset = interrupt_enabled;
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(rhport);
+ hardware_update_device_address(0x00);
+
+ // USBOTG_H_FS->HOST_SETUP = 0x00;
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_BUS_RESET;
+
+ return;
+}
+
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset_end()\r\n");
+
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_BUS_RESET;
+ tusb_time_delay_ms_api(2);
+
+ if ((USBOTG_H_FS->HOST_CTRL & USBFS_UH_PORT_EN) == 0) {
+ if (hcd_port_speed_get(0) == TUSB_SPEED_LOW) {
+ hardware_update_port_speed(TUSB_SPEED_LOW);
+ }
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ // Suppress the attached event
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_DETECT;
+
+ if (int_state_for_portreset) {
+ hcd_int_enable(rhport);
+ }
+}
+
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+
+ return hardware_device_attached();
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+ if (USBOTG_H_FS->MIS_ST & USBFS_UMS_DM_LEVEL) {
+ return TUSB_SPEED_LOW;
+ } else {
+ return TUSB_SPEED_FULL;
+ }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_device_close(%d, 0x%02x)\r\n", rhport, dev_addr);
+ remove_edpt_record_for_device(dev_addr);
+}
+
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+
+ return tusb_time_millis_api();
+}
+
+void hcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBFS_IRQn);
+ interrupt_enabled = true;
+}
+
+void hcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBFS_IRQn);
+ interrupt_enabled = false;
+}
+
+
+static void xfer_retry(void* _params) {
+ LOG_CH32_USBFSH("xfer_retry()\r\n");
+ usb_edpt_t* edpt_info = (usb_edpt_t*)_params;
+ if (usb_current_xfer_info.nak_pending) {
+ usb_current_xfer_info.nak_pending = false;
+ edpt_info->is_nak_pending = false;
+
+ uint8_t dev_addr = edpt_info->dev_addr;
+ uint8_t ep_addr = edpt_info->ep_addr;
+ uint16_t buflen = edpt_info->buflen;
+ uint8_t* buf = edpt_info->buf;
+
+ // Check connectivity
+ usb_edpt_t* edpt_info_current = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info_current) {
+ hcd_edpt_xfer(0, dev_addr, ep_addr, buf, buflen);
+ }
+ }
+}
+
+
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) rhport;
+ (void) in_isr;
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_DETECT) {
+ // Clear the flag
+ USBOTG_H_FS->INT_FG = USBFS_UIF_DETECT;
+ // Read the detection state
+ bool attached = hardware_device_attached();
+ LOG_CH32_USBFSH("hcd_int_handler() attached = %d\r\n", attached ? 1 : 0);
+ if (attached) {
+ hcd_event_device_attach(rhport, true);
+ } else {
+ hcd_event_device_remove(rhport, true);
+ }
+ return;
+ }
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) {
+ // Disable transfer interrupt
+ USBOTG_H_FS->INT_EN &= ~USBFS_UIE_TRANSFER;
+ // Clear the flag
+ // USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ // Copy PID and Endpoint
+ uint8_t pid_edpt = USBOTG_H_FS->HOST_EP_PID;
+ uint8_t status = USBOTG_H_FS->INT_ST;
+ uint8_t dev_addr = USBOTG_H_FS->DEV_ADDR & USBFS_USB_ADDR_MASK;
+ // Clear register to stop transfer
+ // USBOTG_H_FS->HOST_EP_PID = 0x00;
+
+ LOG_CH32_USBFSH("hcd_int_handler() pid_edpt=0x%02x\r\n", pid_edpt);
+
+ uint8_t request_pid = pid_edpt >> 4;
+ uint8_t response_pid = status & USBFS_UIS_H_RES_MASK;
+ uint8_t ep_addr = pid_edpt & 0x0f;
+ if (request_pid == USB_PID_IN) {
+ ep_addr |= 0x80;
+ }
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info == NULL) {
+ PANIC("\r\nget_edpt_record(0x%02x, 0x%02x) returned NULL in USBHD_IRQHandler\r\n", dev_addr, ep_addr);
+ }
+
+ if (status & USBFS_UIS_TOG_OK) {
+ edpt_info->data_toggle ^= 0x01;
+
+ switch (request_pid) {
+ case USB_PID_SETUP:
+ case USB_PID_OUT: {
+ uint16_t tx_len = USBOTG_H_FS->HOST_TX_LEN;
+ usb_current_xfer_info.bufferlen -= tx_len;
+ usb_current_xfer_info.xferred_len += tx_len;
+ if (usb_current_xfer_info.bufferlen == 0) {
+ LOG_CH32_USBFSH("USB_PID_%s completed %d bytes\r\n", request_pid == USB_PID_OUT ? "OUT" : "SETUP", usb_current_xfer_info.xferred_len);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, usb_current_xfer_info.xferred_len, XFER_RESULT_SUCCESS, in_isr);
+ return;
+ } else {
+ LOG_CH32_USBFSH("USB_PID_OUT continue...\r\n");
+ usb_current_xfer_info.buffer += tx_len;
+ uint16_t copylen = TU_MIN(edpt_info->max_packet_size, usb_current_xfer_info.bufferlen);
+ memcpy(USBFS_TX_Buf, usb_current_xfer_info.buffer, copylen);
+ hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ case USB_PID_IN: {
+ uint16_t received_len = USBOTG_H_FS->RX_LEN;
+ usb_current_xfer_info.xferred_len += received_len;
+ uint16_t xferred_len = usb_current_xfer_info.xferred_len;
+ LOG_CH32_USBFSH("Read %d bytes\r\n", received_len);
+ // if (received_len > 0 && (usb_current_xfer_info.buffer == NULL || usb_current_xfer_info.bufferlen == 0)) {
+ // PANIC("Data received but buffer not set\r\n");
+ // }
+ memcpy(usb_current_xfer_info.buffer, USBFS_RX_Buf, received_len);
+ usb_current_xfer_info.buffer += received_len;
+ if ((received_len < edpt_info->max_packet_size) || (xferred_len == usb_current_xfer_info.bufferlen)) {
+ // USB device sent all data.
+ LOG_CH32_USBFSH("USB_PID_IN completed\r\n");
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, XFER_RESULT_SUCCESS, in_isr);
+ return;
+ } else {
+ // USB device may send more data.
+ LOG_CH32_USBFSH("Read more data\r\n");
+ hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ default: {
+ LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ }
+ }
+ } else {
+ if (response_pid == USB_PID_STALL) {
+ LOG_CH32_USBFSH("STALL response\r\n");
+ hcd_edpt_clear_stall(0, dev_addr, ep_addr);
+ edpt_info->data_toggle = 0;
+ hardware_start_xfer(request_pid, ep_addr, 0);
+ return;
+ } else if (response_pid == USB_PID_NAK) {
+ LOG_CH32_USBFSH("NAK reposense\r\n");
+ uint32_t elapsed_time = tusb_time_millis_api() - usb_current_xfer_info.start_ms;
+ (void)elapsed_time;
+ if (edpt_info->xfer_type == TUSB_XFER_INTERRUPT) {
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, in_isr);
+
+ } else {
+ usb_current_xfer_info.is_busy = false;
+ usb_current_xfer_info.nak_pending = true;
+
+
+ edpt_info->is_nak_pending = true;
+ edpt_info->buflen = usb_current_xfer_info.bufferlen;
+ edpt_info->buf = usb_current_xfer_info.buffer;
+
+ hcd_event_t event = {
+ .rhport = rhport,
+ .dev_addr = dev_addr,
+ .event_id = USBH_EVENT_FUNC_CALL,
+ .func_call = {
+ .func = xfer_retry,
+ .param = edpt_info
+ }
+ };
+ hcd_event_handler(&event, in_isr);
+ }
+ return;
+ } else if (response_pid == USB_PID_DATA0 || response_pid == USB_PID_DATA1) {
+ LOG_CH32_USBFSH("Data toggle mismatched and DATA0/1 (not STALL). RX_LEN=%d\r\n", USBOTG_H_FS->RX_LEN);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ } else {
+ LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
+ return;
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc) {
+ (void) rhport;
+ uint8_t ep_addr = ep_desc->bEndpointAddress;
+ uint8_t ep_num = tu_edpt_number(ep_addr);
+ uint16_t max_packet_size = ep_desc->wMaxPacketSize;
+ uint8_t xfer_type = ep_desc->bmAttributes.xfer;
+ LOG_CH32_USBFSH("hcd_edpt_open(rhport=%d, dev_addr=0x%02x, %p) EndpointAdderss=0x%02x,maxPacketSize=%d,xfer_type=%d\r\n", rhport, dev_addr, ep_desc, ep_addr, max_packet_size, xfer_type);
+
+ while (usb_current_xfer_info.is_busy) { }
+
+ if (ep_num == 0x00) {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x00, max_packet_size, xfer_type) != NULL, false);
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x80, max_packet_size, xfer_type) != NULL, false);
+ } else {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type) != NULL, false);
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ hardware_set_port_address_speed(dev_addr);
+
+ return true;
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
+ (void) rhport;
+
+ LOG_CH32_USBFSH("hcd_edpt_xfer(%d, 0x%02x, 0x%02x, ...)\r\n", rhport, dev_addr, ep_addr);
+
+ while (usb_current_xfer_info.is_busy) {}
+ usb_current_xfer_info.is_busy = true;
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ TU_ASSERT(edpt_info != NULL);
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.buffer = buffer;
+ usb_current_xfer_info.bufferlen = buflen;
+ usb_current_xfer_info.start_ms = tusb_time_millis_api();
+ usb_current_xfer_info.xferred_len = 0;
+ usb_current_xfer_info.nak_pending = false;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): READ, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ return hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ } else {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): WRITE, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ uint16_t copylen = TU_MIN(edpt_info->max_packet_size, buflen);
+ USBOTG_H_FS->HOST_TX_LEN = copylen;
+ memcpy(USBFS_TX_Buf, buffer, copylen);
+ return hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ }
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+
+ while (usb_current_xfer_info.is_busy) {}
+
+ usb_current_xfer_info.is_busy = true;
+
+ LOG_CH32_USBFSH("hcd_setup_send(rhport=%d, dev_addr=0x%02x, %p)\r\n", rhport, dev_addr, setup_packet);
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_edpt_t *edpt_info_tx = get_edpt_record(dev_addr, 0x00);
+ usb_edpt_t *edpt_info_rx = get_edpt_record(dev_addr, 0x80);
+ TU_ASSERT(edpt_info_tx != NULL, false);
+ TU_ASSERT(edpt_info_rx != NULL, false);
+
+ // Initialize data toggle (SETUP always starts with DATA0)
+ // Data toggle for OUT is toggled in hcd_int_handler()
+ edpt_info_tx->data_toggle = 0;
+ // Data toggle for IN must be set 0x01 manually.
+ edpt_info_rx->data_toggle = 0x01;
+ const uint16_t setup_packet_datalen = 8;
+ memcpy(USBFS_TX_Buf, setup_packet, setup_packet_datalen);
+ USBOTG_H_FS->HOST_TX_LEN = setup_packet_datalen;
+ uint8_t ep_addr = (setup_packet[0] & 0x80) ? 0x80 : 0x00;
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.start_ms = tusb_time_millis_api();
+ usb_current_xfer_info.buffer = USBFS_TX_Buf;
+ usb_current_xfer_info.bufferlen = setup_packet_datalen;
+ usb_current_xfer_info.xferred_len = 0;
+ usb_current_xfer_info.nak_pending = false;
+
+ hardware_start_xfer(USB_PID_SETUP, 0, 0);
+
+ return true;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall(rhport=%d, dev_addr=0x%02x, ep_addr=0x%02x)\r\n", rhport, dev_addr, ep_addr);
+ uint8_t edpt_num = tu_edpt_number(ep_addr);
+ uint8_t setup_request_clear_stall[8] = {
+ 0x02, 0x01, 0x00, 0x00, edpt_num, 0x00, 0x00, 0x00
+ };
+ memcpy(USBFS_TX_Buf, setup_request_clear_stall, 8);
+ USBOTG_H_FS->HOST_TX_LEN = 8;
+
+ bool prev_int_state = interrupt_enabled;
+ hcd_int_disable(0);
+
+ USBOTG_H_FS->HOST_EP_PID = (USB_PID_SETUP << 4) | 0x00;
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_TRANSFER;
+ while ((USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) == 0) {}
+ USBOTG_H_FS->HOST_EP_PID = 0;
+ uint8_t response_pid = USBOTG_H_FS->INT_ST & USBFS_UIS_H_RES_MASK;
+ (void) response_pid;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall() response pid=0x%02x\r\n", response_pid);
+
+ if (prev_int_state) {
+ hcd_int_enable(0);
+ }
+
+ return true;
+}
+
+#endif
diff --git a/src/tinyusb.mk b/src/tinyusb.mk
index a9f623c24..8f9d52de9 100644
--- a/src/tinyusb.mk
+++ b/src/tinyusb.mk
@@ -12,6 +12,7 @@ TINYUSB_SRC_C += \
src/class/hid/hid_device.c \
src/class/midi/midi_device.c \
src/class/msc/msc_device.c \
+ src/class/mtp/mtp_device.c \
src/class/net/ecm_rndis_device.c \
src/class/net/ncm_device.c \
src/class/usbtmc/usbtmc_device.c \
diff --git a/src/tusb.c b/src/tusb.c
index 13b89997c..6fb5309ab 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -55,6 +55,14 @@ TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) {
#endif
}
+TU_ATTR_WEAK void* tusb_app_virt_to_phys(void *virt_addr) {
+ return virt_addr;
+}
+
+TU_ATTR_WEAK void* tusb_app_phys_to_virt(void *phys_addr) {
+ return phys_addr;
+}
+
//--------------------------------------------------------------------+
// Public API
//--------------------------------------------------------------------+
@@ -109,11 +117,15 @@ bool tusb_inited(void) {
bool ret = false;
#if CFG_TUD_ENABLED
- ret = ret || tud_inited();
+ if (tud_inited()) {
+ ret = true;
+ }
#endif
#if CFG_TUH_ENABLED
- ret = ret || tuh_inited();
+ if (tuh_inited()) {
+ ret = true;
+ }
#endif
return ret;
@@ -136,6 +148,29 @@ void tusb_int_handler(uint8_t rhport, bool in_isr) {
#endif
}
+bool tusb_deinit(uint8_t rhport) {
+ TU_VERIFY(rhport < TUP_USBIP_CONTROLLER_NUM);
+ bool ret = false;
+
+ #if CFG_TUD_ENABLED
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_DEVICE) {
+ TU_ASSERT(tud_deinit(rhport));
+ _tusb_rhport_role[rhport] = TUSB_ROLE_INVALID;
+ ret = true;
+ }
+ #endif
+
+ #if CFG_TUH_ENABLED
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_HOST) {
+ TU_ASSERT(tuh_deinit(rhport));
+ _tusb_rhport_role[rhport] = TUSB_ROLE_INVALID;
+ ret = true;
+ }
+ #endif
+
+ return ret;
+}
+
//--------------------------------------------------------------------+
// Descriptor helper
//--------------------------------------------------------------------+
@@ -178,7 +213,8 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
(void) mutex;
// pre-check to help reducing mutex lock
- TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0));
+ TU_VERIFY(ep_state->busy == 0);
+ TU_VERIFY(ep_state->claimed == 0);
(void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
// can only claim the endpoint if it is not busy and not claimed yet.
@@ -267,7 +303,7 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf,
uint8_t const* p_desc = (uint8_t const*) desc_itf;
uint16_t len = 0;
- while (itf_count--) {
+ while ((itf_count--) > 0) {
// Next on interface desc
len += tu_desc_len(desc_itf);
p_desc = tu_desc_next(p_desc);
@@ -306,7 +342,7 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove
tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable);
#if OSAL_MUTEX_REQUIRED
- if (ff_buf && ff_bufsize) {
+ if (ff_buf != NULL && ff_bufsize > 0) {
osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef);
tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex);
}
@@ -318,11 +354,15 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove
return true;
}
-bool tu_edpt_stream_deinit(tu_edpt_stream_t* s) {
- (void) s;
+bool tu_edpt_stream_deinit(tu_edpt_stream_t *s) {
+ (void)s;
#if OSAL_MUTEX_REQUIRED
- if (s->ff.mutex_wr) osal_mutex_delete(s->ff.mutex_wr);
- if (s->ff.mutex_rd) osal_mutex_delete(s->ff.mutex_rd);
+ if (s->ff.mutex_wr) {
+ osal_mutex_delete(s->ff.mutex_wr);
+ }
+ if (s->ff.mutex_rd) {
+ osal_mutex_delete(s->ff.mutex_rd);
+ }
#endif
return true;
}
@@ -372,7 +412,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool stream_release(uint8_t hwid, tu_edpt_st
bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes) {
// ZLP condition: no pending data, last transferred bytes is multiple of packet size
const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
- TU_VERIFY(!tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (mps - 1))));
+ TU_VERIFY(tu_fifo_empty(&s->ff) && last_xferred_bytes > 0 && (0 == (last_xferred_bytes & (mps - 1))));
TU_VERIFY(stream_claim(hwid, s));
TU_ASSERT(stream_xfer(hwid, s, 0));
return true;
@@ -380,14 +420,13 @@ bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint3
uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
// skip if no data
- TU_VERIFY(tu_fifo_count(&s->ff), 0);
-
+ TU_VERIFY(tu_fifo_count(&s->ff) > 0, 0);
TU_VERIFY(stream_claim(hwid, s), 0);
// Pull data from FIFO -> EP buf
- uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize);
+ const uint16_t count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize);
- if (count) {
+ if (count > 0) {
TU_ASSERT(stream_xfer(hwid, s, count), 0);
return count;
} else {
@@ -398,8 +437,8 @@ uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
}
}
-uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const* buffer, uint32_t bufsize) {
- TU_VERIFY(bufsize); // TODO support ZLP
+uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t *s, const void *buffer, uint32_t bufsize) {
+ TU_VERIFY(bufsize > 0); // TODO support ZLP
if (0 == tu_fifo_depth(&s->ff)) {
// no fifo for buffered
@@ -422,7 +461,7 @@ uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const* buf
}
uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) {
- if (tu_fifo_depth(&s->ff)) {
+ if (tu_fifo_depth(&s->ff) > 0) {
return (uint32_t) tu_fifo_remaining(&s->ff);
} else {
bool is_busy = true;
@@ -478,7 +517,7 @@ uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
}
uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) {
- uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize);
+ const uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t)bufsize);
tu_edpt_stream_read_xfer(hwid, s);
return num_read;
}
@@ -545,8 +584,12 @@ void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) {
if (i % item_per_line == 0) {
// Print Ascii
- if (i != 0) dump_str_line(buf8 - 16, 16);
- for (uint8_t s = 0; s < indent; s++) tu_printf(" ");
+ if (i != 0) {
+ dump_str_line(buf8 - 16, 16);
+ }
+ for (uint8_t s = 0; s < indent; s++) {
+ tu_printf(" ");
+ }
// print offset or absolute address
tu_printf("%04X: ", 16 * i / item_per_line);
}
@@ -561,10 +604,12 @@ void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) {
// fill up last row to 16 for printing ascii
const uint32_t remain = count % 16;
uint8_t nback = (uint8_t) (remain ? remain : 16);
- if (remain) {
+ if (remain > 0) {
for (uint32_t i = 0; i < 16 - remain; i++) {
tu_printf(" ");
- for (int j = 0; j < 2 * size; j++) tu_printf(" ");
+ for (int j = 0; j < 2 * size; j++) {
+ tu_printf(" ");
+ }
}
}
diff --git a/src/tusb.h b/src/tusb.h
index dfba21ddf..62b3b9783 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_H_
-#define _TUSB_H_
+#ifndef TUSB_H_
+#define TUSB_H_
#ifdef __cplusplus
extern "C" {
@@ -88,6 +88,10 @@
#include "class/msc/msc_device.h"
#endif
+ #if CFG_TUD_MTP
+ #include "class/mtp/mtp_device.h"
+ #endif
+
#if CFG_TUD_AUDIO
#include "class/audio/audio_device.h"
#endif
@@ -140,7 +144,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
// Initialize roothub port with device/host role
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
-// Otherwise, it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+// Since USB IRQ handler does use RTOS queue API.
// Note2: defined as macro for backward compatible with tusb_init(void), can be changed to function in the future.
#if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT)
#define _tusb_init_arg0() tusb_rhport_init(0, NULL)
@@ -158,29 +162,20 @@ bool tusb_inited(void);
// Called to handle usb interrupt/event. tusb_init(rhport, role) must be called before
void tusb_int_handler(uint8_t rhport, bool in_isr);
-// TODO
-// bool tusb_teardown(void);
+// Deinit usb stack on roothub port
+bool tusb_deinit(uint8_t rhport);
#else
#define tusb_init(...) (false)
#define tusb_int_handler(...) do {}while(0)
#define tusb_inited() (false)
+#define tusb_deinit(...) (false)
#endif
-//--------------------------------------------------------------------+
-// API Implemented by user
-//--------------------------------------------------------------------+
-
-// Get current milliseconds, required by some port/configuration without RTOS
-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
-void tusb_time_delay_ms_api(uint32_t ms);
-
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_H_ */
+#endif /* TUSB_H_ */
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 98f1a91b5..b90df2724 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -24,14 +24,14 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OPTION_H_
-#define _TUSB_OPTION_H_
+#ifndef TUSB_OPTION_H_
+#define TUSB_OPTION_H_
#include "common/tusb_compiler.h"
// Version is release as major.minor.revision eg 1.0.0
#define TUSB_VERSION_MAJOR 0
-#define TUSB_VERSION_MINOR 18
+#define TUSB_VERSION_MINOR 19
#define TUSB_VERSION_REVISION 0
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
@@ -62,7 +62,8 @@
#define OPT_MCU_LPC55XX OPT_MCU_LPC55
// NRF
-#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
+#define OPT_MCU_NRF5X 100 ///< Nordic nRF 52,53 series
+#define OPT_MCU_NRF54 101 ///< Nordic nRF54 series
// SAM
#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
@@ -70,8 +71,9 @@
#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11
-#define OPT_MCU_SAML22 205 ///< MicroChip SAML22
-#define OPT_MCU_SAML21 206 ///< MicroChip SAML21
+#define OPT_MCU_SAML2X 205 ///< MicroChip SAML2x
+#define OPT_MCU_SAML21 OPT_MCU_SAML2X ///< SAML21 backward compatibility
+#define OPT_MCU_SAML22 OPT_MCU_SAML2X ///< SAML22 backward compatibility
#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family
// STM32
@@ -94,6 +96,9 @@
#define OPT_MCU_STM32U0 316 ///< ST U0
#define OPT_MCU_STM32H7RS 317 ///< ST F7RS
#define OPT_MCU_STM32C0 318 ///< ST C0
+#define OPT_MCU_STM32N6 319 ///< ST N6
+#define OPT_MCU_STM32WBA 320 ///< ST WBA
+#define OPT_MCU_STM32U3 321 ///< ST U3
// Sony
#define OPT_MCU_CXD56 400 ///< SONY CXD56
@@ -127,6 +132,9 @@
#define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2
#define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2
#define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4
+#define OPT_MCU_ESP32C5 908 ///< Espressif ESP32-C5
+#define OPT_MCU_ESP32C61 909 ///< Espressif ESP32-C61
+#define OPT_MCU_ESP32H4 910 ///< Espressif ESP32-H4
#define TUSB_MCU_VENDOR_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
#define TUP_MCU_ESPRESSIF TUSB_MCU_VENDOR_ESPRESSIF // for backward compatibility
@@ -195,14 +203,24 @@
// Analog Devices
#define OPT_MCU_MAX32690 2400 ///< ADI MAX32690
+#define OPT_MCU_MAX32665 2401 ///< ADI MAX32666/5
#define OPT_MCU_MAX32666 2401 ///< ADI MAX32666/5
#define OPT_MCU_MAX32650 2402 ///< ADI MAX32650/1/2
#define OPT_MCU_MAX78002 2403 ///< ADI MAX78002
+// ArteryTek
+#define OPT_MCU_AT32F403A_407 2500 ///< ArteryTek AT32F403A_AT32F407
+#define OPT_MCU_AT32F415 2501 ///< ArteryTek AT32F415
+#define OPT_MCU_AT32F435_437 2502 ///< ArteryTek AT32F435_AT32F437
+#define OPT_MCU_AT32F423 2503 ///< ArteryTek AT32F423
+#define OPT_MCU_AT32F402_405 2504 ///< ArteryTek AT32F402_405
+#define OPT_MCU_AT32F425 2505 ///< ArteryTek AT32F425
+#define OPT_MCU_AT32F413 2506 ///< ArteryTek AT32F413
+
// Check if configured MCU is one of listed
-// Apply _TU_CHECK_MCU with || as separator to list of input
-#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
-#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
+// Apply TU_MCU_IS_EQUAL with || as separator to list of input
+#define TU_MCU_IS_EQUAL(_m) (CFG_TUSB_MCU == (_m))
+#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(TU_MCU_IS_EQUAL, ||, __VA_ARGS__))
//--------------------------------------------------------------------+
// Supported OS
@@ -227,11 +245,11 @@
#define OPT_MODE_HOST 0x0002 ///< Host Mode
// High byte is max operational speed (corresponding to tusb_speed_t)
-#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU
-#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed
-#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed
-#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
-#define OPT_MODE_SPEED_MASK 0xff00
+#define OPT_MODE_DEFAULT_SPEED 0x0000u ///< Default (max) speed supported by MCU
+#define OPT_MODE_LOW_SPEED 0x0100u ///< Low Speed
+#define OPT_MODE_FULL_SPEED 0x0200u ///< Full Speed
+#define OPT_MODE_HIGH_SPEED 0x0400u ///< High Speed
+#define OPT_MODE_SPEED_MASK 0xff00u
//--------------------------------------------------------------------+
// Include tusb_config.h
@@ -267,6 +285,15 @@
#define CFG_TUD_DWC2_DMA_ENABLE CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
#endif
+// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is insufficient).
+#ifndef CFG_TUD_CI_HS_VBUS_CHARGE
+ #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
+ #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
+#endif
+
// Enable DWC2 Slave mode for host
#ifndef CFG_TUH_DWC2_SLAVE_ENABLE
#ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT
@@ -409,7 +436,7 @@
#ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE
#ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
- #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 1
#endif
#define CFG_TUSB_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
@@ -417,7 +444,7 @@
// OS selection
#ifndef CFG_TUSB_OS
- #define CFG_TUSB_OS OPT_OS_NONE
+ #define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_OS_INC_PATH
@@ -493,6 +520,10 @@
#define CFG_TUD_MSC 0
#endif
+#ifndef CFG_TUD_MTP
+ #define CFG_TUD_MTP 0
+#endif
+
#ifndef CFG_TUD_HID
#define CFG_TUD_HID 0
#endif
@@ -591,9 +622,22 @@
// List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
#define CFG_TUH_CDC_FTDI_VID_PID_LIST \
- {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
- {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
- {0x0403, 0xCD18}
+ {0x0403, 0x6001}, /* Similar device to SIO above */ \
+ {0x0403, 0x6006}, /* FTDI's alternate PID for above */ \
+ {0x0403, 0x6010}, /* Dual channel device */ \
+ {0x0403, 0x6011}, /* Quad channel hi-speed device */ \
+ {0x0403, 0x6014}, /* Single channel hi-speed device */ \
+ {0x0403, 0x6015}, /* FT-X series (FT201X, FT230X, FT231X, etc) */ \
+ {0x0403, 0x6040}, /* Dual channel hi-speed device with PD */ \
+ {0x0403, 0x6041}, /* Quad channel hi-speed device with PD */ \
+ {0x0403, 0x6042}, /* Dual channel hi-speed device with PD */ \
+ {0x0403, 0x6043}, /* Quad channel hi-speed device with PD */ \
+ {0x0403, 0x6044}, /* Dual channel hi-speed device with PD */ \
+ {0x0403, 0x6045}, /* Dual channel hi-speed device with PD */ \
+ {0x0403, 0x6048}, /* Quad channel automotive grade hi-speed device */ \
+ {0x0403, 0x8372}, /* Product Id SIO application of 8U100AX */ \
+ {0x0403, 0xFBFA}, /* Product ID for FT232RL */ \
+ {0x0403, 0xCD18}, /* ??? */
#endif
// CP210X is not part of CDC class, only to re-use CDC driver API
@@ -604,7 +648,9 @@
// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
#define CFG_TUH_CDC_CP210X_VID_PID_LIST \
- {0x10C4, 0xEA60}, {0x10C4, 0xEA70}
+ { 0x10C4, 0xEA60 }, /* Silicon Labs factory default */ \
+ { 0x10C4, 0xEA61 }, /* Silicon Labs factory default */ \
+ { 0x10C4, 0xEA70 } /* Silicon Labs Dual Port factory default */
#endif
#ifndef CFG_TUH_CDC_CH34X
@@ -624,6 +670,24 @@
{ 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
#endif
+#ifndef CFG_TUH_CDC_PL2303
+ // PL2303 is not part of CDC class, only to re-use CDC driver API
+ #define CFG_TUH_CDC_PL2303 0
+#endif
+
+#ifndef CFG_TUH_CDC_PL2303_VID_PID_QUIRKS_LIST
+ // List of product IDs that can use the PL2303 CDC driver
+ #define CFG_TUH_CDC_PL2303_VID_PID_LIST \
+ { 0x067b, 0x2303 }, /* initial 2303 */ \
+ { 0x067b, 0x2304 }, /* TB */ \
+ { 0x067b, 0x23a3 }, /* GC */ \
+ { 0x067b, 0x23b3 }, /* GB */ \
+ { 0x067b, 0x23c3 }, /* GT */ \
+ { 0x067b, 0x23d3 }, /* GL */ \
+ { 0x067b, 0x23e3 }, /* GE */ \
+ { 0x067b, 0x23f3 } /* GS */
+#endif
+
#ifndef CFG_TUH_HID
#define CFG_TUH_HID 0
#endif
@@ -664,6 +728,6 @@
// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C)
typedef int make_iso_compilers_happy;
-#endif /* _TUSB_OPTION_H_ */
+#endif /* TUSB_OPTION_H_ */
/** @} */
diff --git a/src/typec/pd_types.h b/src/typec/pd_types.h
index 1b2968f65..950f4d488 100644
--- a/src/typec/pd_types.h
+++ b/src/typec/pd_types.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_PD_TYPES_H_
-#define _TUSB_PD_TYPES_H_
+#ifndef TUSB_PD_TYPES_H_
+#define TUSB_PD_TYPES_H_
#ifdef __cplusplus
extern "C" {
diff --git a/src/typec/tcd.h b/src/typec/tcd.h
index bcbdab8ed..da7ab4b13 100644
--- a/src/typec/tcd.h
+++ b/src/typec/tcd.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_TCD_H_
-#define _TUSB_TCD_H_
+#ifndef TUSB_TCD_H_
+#define TUSB_TCD_H_
#include "common/tusb_common.h"
#include "pd_types.h"
diff --git a/src/typec/usbc.c b/src/typec/usbc.c
index fdf2a0cd6..20abd1700 100644
--- a/src/typec/usbc.c
+++ b/src/typec/usbc.c
@@ -60,6 +60,23 @@ bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* do
bool parse_msg_control(uint8_t rhport, pd_header_t const* header);
//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) {
+ (void) rhport;
+ (void) header;
+ (void) dobj;
+ (void) p_end;
+ return false;
+}
+
+TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header) {
+ (void) rhport;
+ (void) header;
+ return false;
+}
+
+//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
bool tuc_inited(uint8_t rhport) {
@@ -136,17 +153,13 @@ void tuc_task_ext(uint32_t timeout_ms, bool in_isr) {
}
bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) {
- if (tuc_pd_data_received_cb) {
- tuc_pd_data_received_cb(rhport, header, dobj, p_end);
- }
+ tuc_pd_data_received_cb(rhport, header, dobj, p_end);
return true;
}
bool parse_msg_control(uint8_t rhport, pd_header_t const* header) {
- if (tuc_pd_control_received_cb) {
- tuc_pd_control_received_cb(rhport, header);
- }
+ tuc_pd_control_received_cb(rhport, header);
return true;
}
diff --git a/src/typec/usbc.h b/src/typec/usbc.h
index 9fbff9bc6..711119596 100644
--- a/src/typec/usbc.h
+++ b/src/typec/usbc.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_UTCD_H_
-#define _TUSB_UTCD_H_
+#ifndef TUSB_UTCD_H_
+#define TUSB_UTCD_H_
#include "common/tusb_common.h"
#include "pd_types.h"
@@ -63,7 +63,7 @@ void tuc_task (void) {
tuc_task_ext(UINT32_MAX, false);
}
-#ifndef _TUSB_TCD_H_
+#ifndef TUSB_TCD_H_
extern void tcd_int_handler(uint8_t rhport);
#endif
@@ -74,8 +74,8 @@ extern void tcd_int_handler(uint8_t rhport);
// Callbacks
//--------------------------------------------------------------------+
-TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end);
-TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header);
+bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end);
+bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header);
//--------------------------------------------------------------------+
//