summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-04-29 11:02:37 +0700
committerhathach <[email protected]>2026-04-29 11:02:37 +0700
commit8010366808656937ed684af6fedb899a7130e9ba (patch)
tree706f8e6aee003fa6fb9f9804d93eecb146e052b1
parentda9d36fa4cef193abf12b581ac02e1986ea215a7 (diff)
parent4a4420cd070f4460f6f5ed3c17011d78c325d7c0 (diff)
Merge remote-tracking branch 'origin/master' into update-agents-md
-rw-r--r--.gitignore1
-rw-r--r--README.rst2
-rw-r--r--docs/reference/boards.rst2
-rw-r--r--examples/device/CMakeLists.txt1
-rw-r--r--examples/device/audio_4_channel_mic/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test_freertos/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test_multi_rate/src/main.c2
-rw-r--r--examples/device/audio_test_multi_rate/src/usb_descriptors.c4
-rw-r--r--examples/device/cdc_dual_ports/src/usb_descriptors.c25
-rw-r--r--examples/device/cdc_msc/src/usb_descriptors.c22
-rw-r--r--examples/device/cdc_msc_freertos/src/main.c4
-rw-r--r--examples/device/cdc_msc_freertos/src/usb_descriptors.c22
-rw-r--r--examples/device/cdc_msc_throughput/CMakeLists.txt35
-rw-r--r--examples/device/cdc_msc_throughput/Makefile11
-rw-r--r--examples/device/cdc_msc_throughput/src/main.c151
-rw-r--r--examples/device/cdc_msc_throughput/src/tusb_config.h103
-rw-r--r--examples/device/cdc_msc_throughput/src/usb_descriptors.c186
-rw-r--r--examples/device/cdc_uac2/src/uac2_app.c8
-rw-r--r--examples/device/cdc_uac2/src/usb_descriptors.c22
-rw-r--r--examples/device/dfu/skip.txt1
-rw-r--r--examples/device/dynamic_configuration/src/usb_descriptors.c2
-rw-r--r--examples/device/hid_generic_inout/src/usb_descriptors.c2
-rw-r--r--examples/device/midi_test/src/usb_descriptors.c2
-rw-r--r--examples/device/midi_test_freertos/src/usb_descriptors.c2
-rw-r--r--examples/device/msc_dual_lun/src/usb_descriptors.c12
-rw-r--r--examples/device/mtp/src/usb_descriptors.c2
-rw-r--r--examples/device/net_lwip_webserver/src/lwipopts.h20
-rw-r--r--examples/device/net_lwip_webserver/src/tusb_config.h33
-rw-r--r--examples/device/net_lwip_webserver/src/usb_descriptors.c12
-rw-r--r--examples/device/printer_to_cdc/src/usb_descriptors.c21
-rw-r--r--examples/device/uac2_headset/src/main.c8
-rw-r--r--examples/device/uac2_headset/src/usb_descriptors.c15
-rw-r--r--examples/device/uac2_speaker_fb/src/main.c8
-rw-r--r--examples/device/uac2_speaker_fb/src/usb_descriptors.c15
-rw-r--r--examples/device/webusb_serial/src/usb_descriptors.c22
-rw-r--r--examples/dual/dynamic_switch/src/usb_descriptors.c2
-rw-r--r--hw/bsp/board.c4
-rw-r--r--hw/bsp/espressif/boards/family.c8
-rw-r--r--hw/bsp/espressif/family.cmake17
-rw-r--r--hw/bsp/mm32/family.c6
-rw-r--r--hw/bsp/samd11/family.c4
-rw-r--r--hw/bsp/samd2x_l2x/family.c2
-rw-r--r--hw/bsp/samg/family.c4
-rw-r--r--hw/bsp/stm32c0/boards/stm32c071nucleo/board.h2
-rw-r--r--hw/bsp/tm4c/family.c10
-rw-r--r--lib/networking/rndis_reports.c8
-rw-r--r--src/class/mtp/mtp_device.c2
-rw-r--r--src/class/net/ecm_rndis_device.c5
-rw-r--r--src/common/tusb_compiler.h9
-rw-r--r--src/common/tusb_fifo.c8
-rw-r--r--src/common/tusb_mcu.h45
-rw-r--r--src/common/tusb_types.h33
-rw-r--r--src/device/dcd.h5
-rw-r--r--src/device/usbd.c2
-rw-r--r--src/device/usbd_control.c14
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c14
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c42
-rw-r--r--src/portable/mentor/musb/dcd_musb.c667
-rw-r--r--src/portable/mentor/musb/musb_type.h14
-rw-r--r--src/portable/microchip/samg/dcd_samg.c4
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c4
-rw-r--r--src/portable/nuvoton/nuc120/dcd_nuc120.c8
-rw-r--r--src/portable/nuvoton/nuc121/dcd_nuc121.c8
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c6
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.c4
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c10
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c34
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c40
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.c4
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.h4
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h43
-rw-r--r--src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c50
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c16
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h12
-rw-r--r--src/tinyusb.mk1
-rw-r--r--src/tusb_option.h1
-rwxr-xr-xtest/hil/hil_test.py260
-rw-r--r--test/hil/tinyusb.json10
80 files changed, 1597 insertions, 643 deletions
diff --git a/.gitignore b/.gitignore
index 162f9a019..b833191f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,4 @@ Release
BrowseInfo
.cmake_build
README_processed.rst
+.worktrees
diff --git a/README.rst b/README.rst
index 6be9d4873..04998abaa 100644
--- a/README.rst
+++ b/README.rst
@@ -157,6 +157,8 @@ Supported CPUs
| Espressif | S2, S3, H4 | ✔ | ✔ | ✖ | dwc2 | |
| ESP32 +-----------------------------+--------+------+-----------+------------------------+--------------------+
| | P4 | ✔ | ✔ | ✔ | dwc2 | |
+| +-----------------------------+--------+------+-----------+------------------------+--------------------+
+| | S31 | ✔ | ✔ | ✔ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
| GigaDevice | GD32VF103 | ✔ | | ✖ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
diff --git a/docs/reference/boards.rst b/docs/reference/boards.rst
index dbb5942ca..e61c4f98b 100644
--- a/docs/reference/boards.rst
+++ b/docs/reference/boards.rst
@@ -265,7 +265,7 @@ STMicroelectronics
=================== ================================= ========= ================================================================= ======
Board Name Family URL Note
=================== ================================= ========= ================================================================= ======
-stm32c071nucleo STM32C071 Nucleo stm32c0 https://www.st.com/en/evaluation-tools/nucleo-g071rb.html
+stm32c071nucleo STM32C071 Nucleo stm32c0 https://www.st.com/en/evaluation-tools/nucleo-c071rb.html
stm32f070rbnucleo STM32 F070 Nucleo stm32f0 https://www.st.com/en/evaluation-tools/nucleo-f070rb.html
stm32f072disco STM32 F072 Discovery stm32f0 https://www.st.com/en/evaluation-tools/32f072bdiscovery.html
stm32f072eval STM32 F072 Eval stm32f0 https://www.st.com/en/evaluation-tools/stm32072b-eval.html
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index 7173f455e..088872711 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -16,6 +16,7 @@ set(EXAMPLE_LIST
cdc_dual_ports
cdc_msc
cdc_msc_freertos
+ cdc_msc_throughput
cdc_uac2
dfu
dfu_runtime
diff --git a/examples/device/audio_4_channel_mic/src/usb_descriptors.c b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
index 00337eee7..2380ea0ae 100644
--- a/examples/device/audio_4_channel_mic/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
index 3bb93f67d..216cd062a 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test/src/usb_descriptors.c b/examples/device/audio_test/src/usb_descriptors.c
index ad161939e..37ebf84d3 100644
--- a/examples/device/audio_test/src/usb_descriptors.c
+++ b/examples/device/audio_test/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test_freertos/src/usb_descriptors.c b/examples/device/audio_test_freertos/src/usb_descriptors.c
index ad161939e..37ebf84d3 100644
--- a/examples/device/audio_test_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_test_freertos/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c
index 952176997..a86beb415 100644
--- a/examples/device/audio_test_multi_rate/src/main.c
+++ b/examples/device/audio_test_multi_rate/src/main.c
@@ -532,7 +532,7 @@ static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
//uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const alt = tu_u16_low(p_request->wValue);
// Clear buffer when streaming format is changed
if (alt != 0) {
diff --git a/examples/device/audio_test_multi_rate/src/usb_descriptors.c b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
index 505936fdb..31333dcd3 100644
--- a/examples/device/audio_test_multi_rate/src/usb_descriptors.c
+++ b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
@@ -88,6 +88,10 @@ enum {
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c
index e6011c35a..adfd8cf9d 100644
--- a/examples/device/cdc_dual_ports/src/usb_descriptors.c
+++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c
@@ -106,16 +106,27 @@ enum {
#define EPNUM_CDC_1_OUT 0x05
#define EPNUM_CDC_1_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_0_NOTIF 0x81
- #define EPNUM_CDC_0_OUT 0x02
- #define EPNUM_CDC_0_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x08
+ #define EPNUM_CDC_0_IN 0x89
- #define EPNUM_CDC_1_NOTIF 0x84
- #define EPNUM_CDC_1_OUT 0x05
- #define EPNUM_CDC_1_IN 0x86
+ #define EPNUM_CDC_1_NOTIF 0x82
+ #define EPNUM_CDC_1_OUT 0x0A
+ #define EPNUM_CDC_1_IN 0x8B
+ #else
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x02
+ #define EPNUM_CDC_0_IN 0x83
+
+ #define EPNUM_CDC_1_NOTIF 0x84
+ #define EPNUM_CDC_1_OUT 0x05
+ #define EPNUM_CDC_1_IN 0x86
+ #endif
#else
#define EPNUM_CDC_0_NOTIF 0x81
diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c
index c668ea3a7..5dc80dee3 100644
--- a/examples/device/cdc_msc/src/usb_descriptors.c
+++ b/examples/device/cdc_msc/src/usb_descriptors.c
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index 4fb209fd0..f2f71d089 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -34,10 +34,10 @@
#define USBD_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
- #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBD_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
-#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1))
+#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
//--------------------------------------------------------------------+
diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
index 4950f02e0..f5b015051 100644
--- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c
+++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/cdc_msc_throughput/CMakeLists.txt b/examples/device/cdc_msc_throughput/CMakeLists.txt
new file mode 100644
index 000000000..69c1caa6a
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(cdc_msc_throughput C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+if (RTOS STREQUAL zephyr)
+ set(EXE_NAME app)
+else()
+ set(EXE_NAME ${PROJECT_NAME})
+ add_executable(${EXE_NAME})
+endif()
+
+# Example source
+target_sources(${EXE_NAME} PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${EXE_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${EXE_NAME} ${RTOS})
diff --git a/examples/device/cdc_msc_throughput/Makefile b/examples/device/cdc_msc_throughput/Makefile
new file mode 100644
index 000000000..035e90308
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/Makefile
@@ -0,0 +1,11 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_msc_throughput/src/main.c b/examples/device/cdc_msc_throughput/src/main.c
new file mode 100644
index 000000000..116cbe13f
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/main.c
@@ -0,0 +1,151 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+// cdc_msc_throughput: minimal CDC+MSC device aimed at measuring pure USB bulk throughput.
+// MSC read/write callbacks don't touch any backing storage - write discards the
+// data and read only zero-fills the low LBAs the host scans during enumeration
+// (partition table, GPT header). Higher LBAs return whatever is already in the
+// transfer buffer, so `dd` numbers reflect the USB/driver ceiling, not any
+// simulated storage or per-byte memset cost.
+// CDC path drains RX in tud_cdc_rx_cb and sources TX from a static filler in the
+// main loop so `dd` can target /dev/ttyACMx in either direction.
+
+static void cdc_throughput_task(void);
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+int main(void) {
+ board_init();
+
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+
+ while (1) {
+ tud_task();
+ cdc_throughput_task();
+ }
+}
+
+//--------------------------------------------------------------------+
+// CDC callbacks + tasks
+//--------------------------------------------------------------------+
+void tud_cdc_rx_cb(uint8_t itf) {
+ (void) itf;
+ tud_cdc_read_flush(); // Drain RX
+}
+
+static void cdc_throughput_task(void) {
+ if (!tud_cdc_connected()) return;
+
+ // Source TX: fill whatever write room is free.
+ static uint8_t const filler[CFG_TUD_CDC_TX_EPSIZE] = {0};
+ uint32_t room = tud_cdc_write_available();
+ while (room > 0) {
+ uint32_t n = tud_cdc_write(filler, tu_min32(room, sizeof(filler)));
+ if (n == 0) {
+ break;
+ }
+ room -= n;
+ }
+ tud_cdc_write_flush();
+}
+
+//--------------------------------------------------------------------+
+// MSC callbacks
+//--------------------------------------------------------------------+
+
+// 1 GiB logical capacity so `dd` can run long enough for stable numbers.
+// No real backing store - block content is synthesised on read, discarded on write.
+enum {
+ DISK_BLOCK_SIZE = 512,
+ DISK_BLOCK_COUNT = 0x00200000u, // 2 Mi blocks = 1 GiB
+ // Kernel probes partition-table / filesystem-superblock locations near the
+ // start of the disk during enumeration. Zero-fill only this head range so the
+ // block layer sees "no partition, no filesystem" and leaves us alone; higher
+ // LBAs skip the memset so `dd` measures pure USB/driver throughput.
+ DISK_ZEROFILL_LBA = 64, // 32 KiB
+};
+
+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;
+ const char vid[] = "TinyUSB";
+ const char pid[] = "Mass Storage";
+ const char rev[] = "1.0";
+ (void) strncpy((char*) vendor_id, vid, 8);
+ (void) strncpy((char*) product_id, pid, 16);
+ (void) strncpy((char*) product_rev, rev, 4);
+}
+
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
+ (void) lun;
+ *block_count = DISK_BLOCK_COUNT;
+ *block_size = DISK_BLOCK_SIZE;
+}
+
+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;
+}
+
+bool tud_msc_is_writable_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+// READ10: zero-fill only the head range the kernel inspects, skip memset everywhere
+// else so we measure the USB / driver path rather than memset cost.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
+ (void) lun; (void) offset;
+ if (lba < DISK_ZEROFILL_LBA) {
+ memset(buffer, 0, bufsize);
+ } else {
+ (void) buffer;
+ }
+ return (int32_t) bufsize;
+}
+
+// WRITE10: discard the received data entirely - this is the pure USB-speed test.
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
+ (void) lun; (void) lba; (void) offset; (void) buffer;
+ return (int32_t) bufsize;
+}
+
+// Unknown SCSI commands: stall with Invalid Command sense.
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ (void) scsi_cmd; (void) buffer; (void) bufsize;
+ tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ return -1;
+}
diff --git a/examples/device/cdc_msc_throughput/src/tusb_config.h b/examples/device/cdc_msc_throughput/src/tusb_config.h
new file mode 100644
index 000000000..6c8655719
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/tusb_config.h
@@ -0,0 +1,103 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Board Specific Configuration
+//--------------------------------------------------------------------
+
+#ifndef BOARD_TUD_RHPORT
+ #define BOARD_TUD_RHPORT 0
+#endif
+
+#ifndef BOARD_TUD_MAX_SPEED
+ #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 1
+
+// Large MSC bulk buffer: host transfers big CBW payloads (e.g. dd bs=1M does 64KiB
+// chunks). A 4K per-bulk-IO buffer lets the class driver amortise the per-CBW
+// overhead across many USB packets, approximating the maximum USB bulk throughput.
+#define CFG_TUD_MSC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 4096 : 1024)
+
+// #define CFG_TUD_CDC_TX_PERSISTENT 1
+
+// CDC throughput: size for HS; tinyusb will auto-scale for FS via TUD_OPT_HIGH_SPEED.
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_RX_EPSIZE
+
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_BUFSIZE CFG_TUD_CDC_RX_BUFSIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_msc_throughput/src/usb_descriptors.c b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
new file mode 100644
index 000000000..3b0ff6e17
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
@@ -0,0 +1,186 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#define USB_PID (0x4000 | ((CFG_TUD_CDC) ? (1 << 0) : 0) | ((CFG_TUD_MSC) ? (1 << 1) : 0))
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ // IAD required for composite CDC + MSC
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01,
+};
+
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+enum {
+ ITF_NUM_CDC = 0,
+ ITF_NUM_CDC_DATA,
+ ITF_NUM_MSC,
+ ITF_NUM_TOTAL,
+};
+
+// Place bulk endpoints on EP>=8 for MAX32690 class parts (bigger FIFO, DPB-capable).
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
+#else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_MSC_OUT 0x03
+ #define EPNUM_MSC_IN 0x83
+#endif
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
+
+static uint8_t const desc_fs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t const desc_hs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
+};
+
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00,
+};
+
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+ return desc_other_speed_config;
+}
+#endif
+
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;
+#if TUD_OPT_HIGH_SPEED
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+};
+
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 },
+ "TinyUSB",
+ "Throughput",
+ NULL,
+ "TinyUSB CDC",
+ "TinyUSB MSC",
+};
+
+static uint16_t _desc_str[32 + 1];
+
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
+ const char *str = string_desc_arr[index];
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
+ if (chr_count > max_count) chr_count = max_count;
+ for (size_t i = 0; i < chr_count; i++) _desc_str[1 + i] = str[i];
+ break;
+ }
+
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c
index 7760c402b..6e9d1d9e3 100644
--- a/examples/device/cdc_uac2/src/uac2_app.c
+++ b/examples/device/cdc_uac2/src/uac2_app.c
@@ -263,8 +263,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
{
(void)rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
// Audio streaming stop
@@ -277,8 +277,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c
index e6caaa971..fdffc761e 100644
--- a/examples/device/cdc_uac2/src/usb_descriptors.c
+++ b/examples/device/cdc_uac2/src/usb_descriptors.c
@@ -97,15 +97,25 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put CDC bulk on EP>=8 and audio iso on EP10/11 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
- #define EPNUM_CDC_NOTIF 0x83
- #define EPNUM_CDC_OUT 0x04
- #define EPNUM_CDC_IN 0x85
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x04
+ #define EPNUM_CDC_IN 0x85
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
diff --git a/examples/device/dfu/skip.txt b/examples/device/dfu/skip.txt
index 79d3da9d2..ccff857ac 100644
--- a/examples/device/dfu/skip.txt
+++ b/examples/device/dfu/skip.txt
@@ -1,3 +1,2 @@
-mcu:TM4C
mcu:BCM2835
family:espressif
diff --git a/examples/device/dynamic_configuration/src/usb_descriptors.c b/examples/device/dynamic_configuration/src/usb_descriptors.c
index 458b7c2a5..c4049414f 100644
--- a/examples/device/dynamic_configuration/src/usb_descriptors.c
+++ b/examples/device/dynamic_configuration/src/usb_descriptors.c
@@ -132,7 +132,7 @@ enum
#define EPNUM_1_MSC_OUT 0x02
#define EPNUM_1_MSC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_0_CDC_NOTIF 0x81
diff --git a/examples/device/hid_generic_inout/src/usb_descriptors.c b/examples/device/hid_generic_inout/src/usb_descriptors.c
index 929b2fd3a..93e718461 100644
--- a/examples/device/hid_generic_inout/src/usb_descriptors.c
+++ b/examples/device/hid_generic_inout/src/usb_descriptors.c
@@ -97,7 +97,7 @@ enum
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
-#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_HID_OUT 0x01
diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c
index e969f33a3..99c798ce1 100644
--- a/examples/device/midi_test/src/usb_descriptors.c
+++ b/examples/device/midi_test/src/usb_descriptors.c
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c
index e969f33a3..99c798ce1 100644
--- a/examples/device/midi_test_freertos/src/usb_descriptors.c
+++ b/examples/device/midi_test_freertos/src/usb_descriptors.c
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c
index f73935ee0..b328cf17f 100644
--- a/examples/device/msc_dual_lun/src/usb_descriptors.c
+++ b/examples/device/msc_dual_lun/src/usb_descriptors.c
@@ -91,11 +91,17 @@ enum
#define EPNUM_MSC_OUT 0x02
#define EPNUM_MSC_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_MSC_OUT 0x01
- #define EPNUM_MSC_IN 0x82
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_MSC_OUT 0x08
+ #define EPNUM_MSC_IN 0x89
+ #else
+ #define EPNUM_MSC_OUT 0x01
+ #define EPNUM_MSC_IN 0x82
+ #endif
#else
#define EPNUM_MSC_OUT 0x01
diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c
index f0aa3de6b..4c840560e 100644
--- a/examples/device/mtp/src/usb_descriptors.c
+++ b/examples/device/mtp/src/usb_descriptors.c
@@ -94,7 +94,7 @@ enum
#define EPNUM_MTP_OUT 0x02
#define EPNUM_MTP_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MTP_EVT 0x81
diff --git a/examples/device/net_lwip_webserver/src/lwipopts.h b/examples/device/net_lwip_webserver/src/lwipopts.h
index 11686ce2a..350120423 100644
--- a/examples/device/net_lwip_webserver/src/lwipopts.h
+++ b/examples/device/net_lwip_webserver/src/lwipopts.h
@@ -32,6 +32,14 @@
#ifndef LWIPOPTS_H__
#define LWIPOPTS_H__
+// Pulls in tusb_option.h → tusb_config.h, which defines LWIP_HIGH_THROUGHPUT
+// based on the target MCU's SRAM tier.
+#include "tusb_option.h"
+
+#ifndef LWIP_HIGH_THROUGHPUT
+ #define LWIP_HIGH_THROUGHPUT 0
+#endif
+
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
#define MEM_ALIGNMENT 4
@@ -49,7 +57,15 @@
#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF (4 * TCP_MSS)
-#define TCP_WND (4 * TCP_MSS)
+#if LWIP_HIGH_THROUGHPUT
+ #define TCP_WND (8 * TCP_MSS)
+ #define PBUF_POOL_SIZE 8
+ // Must grow in step with TCP_SND_BUF (default MEMP_NUM_TCP_SEG=16 caps TCP_SND_BUF at 4*MSS).
+ #define MEMP_NUM_TCP_SEG 16
+#else
+ #define TCP_WND (4 * TCP_MSS)
+ #define PBUF_POOL_SIZE 4
+#endif
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
@@ -60,8 +76,6 @@
#define LWIP_SINGLE_NETIF 1
#define LWIP_NETIF_LINK_CALLBACK 1
-#define PBUF_POOL_SIZE 4
-
#define HTTPD_USE_CUSTOM_FSDATA 0
#define LWIP_MULTICAST_PING 1
diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h
index 3285ea52c..24082fe25 100644
--- a/examples/device/net_lwip_webserver/src/tusb_config.h
+++ b/examples/device/net_lwip_webserver/src/tusb_config.h
@@ -92,32 +92,53 @@ extern "C" {
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
#define USE_ECM 1
-#elif TU_CHECK_MCU(OPT_MCU_MAX32690, OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX78002)
- #define USE_ECM 1
#else
#define USE_ECM 0
- #define INCLUDE_IPERF
#endif
#endif
+// MCU SRAM tier — drives the bigger lwIP buffers in lwipopts.h, the larger
+// NCM OUT NTB size below, and whether iperf is built. Small-RAM MCUs
+// (stm32c0/f1/wb, lpc11/13, samd11) keep modest defaults to fit.
+#ifndef LWIP_HIGH_THROUGHPUT
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) || \
+ TU_CHECK_MCU(OPT_MCU_STM32F2, OPT_MCU_STM32F4, OPT_MCU_STM32F7) || \
+ TU_CHECK_MCU(OPT_MCU_STM32H5, OPT_MCU_STM32H7, OPT_MCU_STM32H7RS) || \
+ TU_CHECK_MCU(OPT_MCU_STM32U5, OPT_MCU_STM32N6) || \
+ TU_CHECK_MCU(OPT_MCU_RP2040) || \
+ TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) || \
+ TU_CHECK_MCU(OPT_MCU_NRF5X)
+ #define LWIP_HIGH_THROUGHPUT 1
+ #else
+ #define LWIP_HIGH_THROUGHPUT 0
+ #endif
+#endif
+
+#if LWIP_HIGH_THROUGHPUT && !defined(INCLUDE_IPERF)
+ #define INCLUDE_IPERF
+#endif
+
//--------------------------------------------------------------------
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
//--------------------------------------------------------------------
// Must be >> MTU
// Can be set to 2048 without impact
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (1 * TCP_MSS + 100)
// Must be >> MTU
// Can be set to smaller values if wNtbOutMaxDatagrams==1
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+#if LWIP_HIGH_THROUGHPUT
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (3 * TCP_MSS + 100)
+#else
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+#endif
// Number of NCM transfer blocks for reception side
#ifndef CFG_TUD_NCM_OUT_NTB_N
#define CFG_TUD_NCM_OUT_NTB_N 1
#endif
-// Number of NCM transfer blocks for transmission side
#ifndef CFG_TUD_NCM_IN_NTB_N
#define CFG_TUD_NCM_IN_NTB_N 1
#endif
diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c
index c976cb62b..e97b103f9 100644
--- a/examples/device/net_lwip_webserver/src/usb_descriptors.c
+++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c
@@ -121,12 +121,20 @@ const uint8_t *tud_descriptor_device_cb(void) {
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
-// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+// MCUs that don't support the same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
+
+#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+// endpoint 8,9 has FIFO of 2048 bytes
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x08
+#define EPNUM_NET_IN 0x89
+#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x83
+#endif
#else
#define EPNUM_NET_NOTIF 0x81
diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c
index 30d309ed4..db7bfe97a 100644
--- a/examples/device/printer_to_cdc/src/usb_descriptors.c
+++ b/examples/device/printer_to_cdc/src/usb_descriptors.c
@@ -67,12 +67,21 @@ uint8_t const *tud_descriptor_device_cb(void) {
//--------------------------------------------------------------------+
// Endpoint numbers
-#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
- #define EPNUM_PRINTER_OUT 0x04
- #define EPNUM_PRINTER_IN 0x85
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_PRINTER_OUT 0x0A
+ #define EPNUM_PRINTER_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_PRINTER_OUT 0x04
+ #define EPNUM_PRINTER_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c
index 0ea63d8f7..779e927bc 100644
--- a/examples/device/uac2_headset/src/main.c
+++ b/examples/device/uac2_headset/src/main.c
@@ -522,8 +522,8 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
blink_interval_ms = BLINK_MOUNTED;
@@ -534,8 +534,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c
index e4fbbf8a5..b554e7195 100644
--- a/examples/device/uac2_headset/src/usb_descriptors.c
+++ b/examples/device/uac2_headset/src/usb_descriptors.c
@@ -97,12 +97,19 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO_OUT 0x08
#define EPNUM_AUDIO_INT 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
- #define EPNUM_AUDIO_INT 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
+ #define EPNUM_AUDIO_INT 0x01
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+ #define EPNUM_AUDIO_INT 0x03
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c
index c3e97bb28..402642162 100644
--- a/examples/device/uac2_speaker_fb/src/main.c
+++ b/examples/device/uac2_speaker_fb/src/main.c
@@ -457,8 +457,8 @@ static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0)
@@ -531,8 +531,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
- uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
- uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) {
blink_interval_ms = BLINK_MOUNTED;
diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
index c5a161a1e..f0c780e38 100644
--- a/examples/device/uac2_speaker_fb/src/usb_descriptors.c
+++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
@@ -115,12 +115,19 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
#define EPNUM_AUDIO_FB 0x08
#define EPNUM_DEBUG 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO 0x02
- #define EPNUM_AUDIO_FB 0x01
- #define EPNUM_DEBUG 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+ #define EPNUM_AUDIO_FB 0x0B
+ #define EPNUM_DEBUG 0x01
+ #else
+ #define EPNUM_AUDIO 0x02
+ #define EPNUM_AUDIO_FB 0x01
+ #define EPNUM_DEBUG 0x03
+ #endif
#else
#define EPNUM_AUDIO 0x01
diff --git a/examples/device/webusb_serial/src/usb_descriptors.c b/examples/device/webusb_serial/src/usb_descriptors.c
index 0ef41a68e..527837161 100644
--- a/examples/device/webusb_serial/src/usb_descriptors.c
+++ b/examples/device/webusb_serial/src/usb_descriptors.c
@@ -104,15 +104,25 @@ enum
#define EPNUM_VENDOR_OUT 0x05
#define EPNUM_VENDOR_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+
+ #define EPNUM_VENDOR_OUT 0x0A
+ #define EPNUM_VENDOR_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
- #define EPNUM_VENDOR_OUT 0x04
- #define EPNUM_VENDOR_IN 0x85
+ #define EPNUM_VENDOR_OUT 0x04
+ #define EPNUM_VENDOR_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/dual/dynamic_switch/src/usb_descriptors.c b/examples/dual/dynamic_switch/src/usb_descriptors.c
index 54ffc2c18..ef6d795b7 100644
--- a/examples/dual/dynamic_switch/src/usb_descriptors.c
+++ b/examples/dual/dynamic_switch/src/usb_descriptors.c
@@ -86,7 +86,7 @@ enum {
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_CDC_NOTIF 0x81
diff --git a/hw/bsp/board.c b/hw/bsp/board.c
index ae58bb5fc..65b44e5f2 100644
--- a/hw/bsp/board.c
+++ b/hw/bsp/board.c
@@ -51,12 +51,12 @@ int sys_read(int fhdl, char *buf, size_t count) TU_ATTR_USED;
int sys_write(int fhdl, const char *buf, size_t count) {
(void) fhdl;
- return (int) SEGGER_RTT_Write(0, buf, (int) count);
+ return (int) SEGGER_RTT_Write(0, buf, (unsigned) count);
}
int sys_read(int fhdl, char *buf, size_t count) {
(void) fhdl;
- int rd = (int) SEGGER_RTT_Read(0, buf, count);
+ int rd = (int) SEGGER_RTT_Read(0, buf, (unsigned) count);
return (rd > 0) ? rd : -1;
}
#endif
diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c
index 48b1253b6..4b28cecf2 100644
--- a/hw/bsp/espressif/boards/family.c
+++ b/hw/bsp/espressif/boards/family.c
@@ -49,7 +49,7 @@ static led_strip_handle_t led_strip;
static void max3421_init(void);
#endif
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4)
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4, OPT_MCU_ESP32S31)
static bool usb_init(uint8_t rhport, bool is_host);
#endif
@@ -111,10 +111,6 @@ void board_init(void) {
#endif
}
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4)
-
-#endif
-
//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+
@@ -178,7 +174,7 @@ void board_reset_to_bootloader(void) {
// PHY Init
//--------------------------------------------------------------------
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4)
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4, OPT_MCU_ESP32S31)
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
#include "esp_private/usb_phy.h"
diff --git a/hw/bsp/espressif/family.cmake b/hw/bsp/espressif/family.cmake
index ca9eadaf6..30d5a6ac9 100644
--- a/hw/bsp/espressif/family.cmake
+++ b/hw/bsp/espressif/family.cmake
@@ -2,7 +2,10 @@
include("${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake")
string(TOUPPER ${IDF_TARGET} FAMILY_MCUS)
-# Device port default to Port1 for P4 (highspeed), Port0 for others (fullspeed)
+# Device/host port defaults:
+# - ESP32-P4 uses Port1 (highspeed)
+# - ESP32-S31 uses Port0 (highspeed)
+# - Other targets use Port0 and derive the default speed from RHPORT_SPEED
set(RHPORT_SPEED OPT_MODE_FULL_SPEED OPT_MODE_HIGH_SPEED)
if (NOT DEFINED RHPORT_DEVICE)
@@ -22,10 +25,18 @@ if (NOT DEFINED RHPORT_HOST)
endif()
if (NOT DEFINED RHPORT_DEVICE_SPEED)
- list(GET RHPORT_SPEED ${RHPORT_DEVICE} RHPORT_DEVICE_SPEED)
+ if (IDF_TARGET STREQUAL "esp32s31")
+ set(RHPORT_DEVICE_SPEED OPT_MODE_HIGH_SPEED)
+ else ()
+ list(GET RHPORT_SPEED ${RHPORT_DEVICE} RHPORT_DEVICE_SPEED)
+ endif ()
endif ()
if (NOT DEFINED RHPORT_HOST_SPEED)
- list(GET RHPORT_SPEED ${RHPORT_HOST} RHPORT_HOST_SPEED)
+ if (IDF_TARGET STREQUAL "esp32s31")
+ set(RHPORT_HOST_SPEED OPT_MODE_HIGH_SPEED)
+ else ()
+ list(GET RHPORT_SPEED ${RHPORT_HOST} RHPORT_HOST_SPEED)
+ endif ()
endif ()
# Add example src and bsp directories
diff --git a/hw/bsp/mm32/family.c b/hw/bsp/mm32/family.c
index 651c9496e..14a17f6c5 100644
--- a/hw/bsp/mm32/family.c
+++ b/hw/bsp/mm32/family.c
@@ -59,11 +59,11 @@ void OTG_FS_IRQHandler(void) {
void USB_DeviceClockInit(void) {
/* Select USBCLK source */
// RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
- RCC->CFGR &= ~(0x3 << 22);
- RCC->CFGR |= (0x1 << 22);
+ RCC->CFGR &= ~(0x3U << 22);
+ RCC->CFGR |= (0x1U << 22);
/* Enable USB clock */
- RCC->AHB2ENR |= 0x1 << 7;
+ RCC->AHB2ENR |= 0x1U << 7;
}
void board_init(void) {
diff --git a/hw/bsp/samd11/family.c b/hw/bsp/samd11/family.c
index 0c987b85a..0864aa3dd 100644
--- a/hw/bsp/samd11/family.c
+++ b/hw/bsp/samd11/family.c
@@ -34,6 +34,8 @@
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#include "hal/include/hal_gpio.h"
@@ -66,7 +68,7 @@ void USB_Handler(void)
//--------------------------------------------------------------------+
/* Referenced GCLKs, should be initialized firstly */
-#define _GCLK_INIT_1ST (1 << 0 | 1 << 1)
+#define _GCLK_INIT_1ST (1u << 0 | 1u << 1)
/* Not referenced GCLKs, initialized last */
#define _GCLK_INIT_LAST (~_GCLK_INIT_1ST)
diff --git a/hw/bsp/samd2x_l2x/family.c b/hw/bsp/samd2x_l2x/family.c
index 11b6343cd..306ea2308 100644
--- a/hw/bsp/samd2x_l2x/family.c
+++ b/hw/bsp/samd2x_l2x/family.c
@@ -79,7 +79,7 @@
#ifdef SAMD21_FAMILY
/* Referenced GCLKs, should be initialized firstly */
-#define _GCLK_INIT_1ST (1 << 0 | 1 << 1)
+#define _GCLK_INIT_1ST (1u << 0 | 1u << 1)
/* Not referenced GCLKs, initialized last */
#define _GCLK_INIT_LAST (~_GCLK_INIT_1ST)
#endif
diff --git a/hw/bsp/samg/family.c b/hw/bsp/samg/family.c
index 519068986..9edc43b51 100644
--- a/hw/bsp/samg/family.c
+++ b/hw/bsp/samg/family.c
@@ -27,15 +27,17 @@
manufacturer: Microchip
*/
-#include "sam.h"
// Suppress warning caused by mcu driver
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
+#include "sam.h"
#include "peripheral_clk_config.h"
#include "hal/include/hal_init.h"
#include "hal/include/hpl_usart_sync.h"
diff --git a/hw/bsp/stm32c0/boards/stm32c071nucleo/board.h b/hw/bsp/stm32c0/boards/stm32c071nucleo/board.h
index 085d0d721..b62fc3a2a 100644
--- a/hw/bsp/stm32c0/boards/stm32c071nucleo/board.h
+++ b/hw/bsp/stm32c0/boards/stm32c071nucleo/board.h
@@ -27,7 +27,7 @@
/* metadata:
name: STM32C071 Nucleo
- url: https://www.st.com/en/evaluation-tools/nucleo-g071rb.html
+ url: https://www.st.com/en/evaluation-tools/nucleo-c071rb.html
*/
#ifndef BOARD_H_
diff --git a/hw/bsp/tm4c/family.c b/hw/bsp/tm4c/family.c
index 6988a264e..4f85be220 100644
--- a/hw/bsp/tm4c/family.c
+++ b/hw/bsp/tm4c/family.c
@@ -36,7 +36,7 @@ static void board_uart_init(void) {
// BAUDRATE = 115200, with SystemCoreClock = 50 Mhz refer manual for calculation
// - BRDI = SystemCoreClock / (16* baud)
// - BRDF = int(fraction*64 + 0.5)
- UART0->CTL &= ~(1 << 0); // Disable UART0 by clearing UARTEN bit in the UARTCTL register
+ UART0->CTL &= ~(1U << 0); // Disable UART0 by clearing UARTEN bit in the UARTCTL register
UART0->IBRD = 27; // Write the integer portion of the BRD to the UARTIRD register
UART0->FBRD = 8; // Write the fractional portion of the BRD to the UARTFBRD registerer
@@ -58,6 +58,14 @@ static void board_button_init(GPIOA_Type* port, uint8_t PinMsk) {
/* Set direction */
port->DIR &= ~PinMsk;
+
+ /* Enable internal pull so the idle state is deterministic. LaunchPad buttons
+ * connect the pin to GND when pressed (active-low) and require a pull-up. */
+#if BUTTON_STATE_ACTIVE == 0
+ port->PUR |= PinMsk;
+#else
+ port->PDR |= PinMsk;
+#endif
}
static void board_led_init(GPIOA_Type* port, uint8_t PinMsk, uint8_t dirmsk) {
diff --git a/lib/networking/rndis_reports.c b/lib/networking/rndis_reports.c
index 5e824d5a5..f06bc5507 100644
--- a/lib/networking/rndis_reports.c
+++ b/lib/networking/rndis_reports.c
@@ -36,9 +36,13 @@
#include "rndis_protocol.h"
#include "netif/ethernet.h"
-#define RNDIS_LINK_SPEED 12000000 /* Link baudrate (12Mbit/s for USB-FS) */
#define RNDIS_VENDOR "TinyUSB" /* NIC vendor name */
+// USB link speed in bits/sec, reflected to host via OID_GEN_LINK_SPEED.
+static inline uint32_t rndis_link_speed_bps(void) {
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? 480000000U : 12000000U;
+}
+
static const uint8_t *const station_hwaddr = tud_network_mac_address;
static const uint8_t *const permanent_hwaddr = tud_network_mac_address;
@@ -127,7 +131,7 @@ static void rndis_query(void)
case OID_GEN_MEDIA_IN_USE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return;
case OID_GEN_PHYSICAL_MEDIUM: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return;
case OID_GEN_HARDWARE_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
- case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, RNDIS_LINK_SPEED / 100); return;
+ case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, rndis_link_speed_bps() / 100U); return;
case OID_GEN_VENDOR_ID: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00FFFFFF); return;
case OID_GEN_VENDOR_DESCRIPTION: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, rndis_vendor, strlen(rndis_vendor) + 1); return;
case OID_GEN_CURRENT_PACKET_FILTER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, oid_packet_filter); return;
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
index 59096e476..0da984f4a 100644
--- a/src/class/mtp/mtp_device.c
+++ b/src/class/mtp/mtp_device.c
@@ -321,7 +321,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
.session_id = p_mtp->session_id,
.request = request,
.buf = p_mtp->control_buf,
- .bufsize = tu_le16toh(request->wLength),
+ .bufsize = request->wLength,
};
switch (request->bRequest) {
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index eaa82c187..9282e0605 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -206,14 +206,15 @@ static void ecm_report(bool nc) {
},
};
+ const uint32_t link_bps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 480000000U : 12000000U;
const ecm_notify_t ecm_notify_csc = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */
.wLength = 8,
},
- .downlink = 9728000,
- .uplink = 9728000,
+ .downlink = link_bps,
+ .uplink = link_bps,
};
ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc;
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index f20834cea..a8971c3df 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -66,6 +66,9 @@
#define TU_LITTLE_ENDIAN (0x12u)
#define TU_BIG_ENDIAN (0x21u)
+#define TU_BITFIELD_LE (0x34u)
+#define TU_BITFIELD_BE (0x43u)
+
/*------------------------------------------------------------------*/
/* Count number of arguments of __VA_ARGS__
* - reference www.stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments
@@ -167,8 +170,10 @@
// For TI ARM compiler, __BYTE_ORDER__ is not defined for MSP430 but still LE
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__MSP430__)
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_LE
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_BE
#endif
// Unfortunately XC16 doesn't provide builtins for 32bit endian conversion
@@ -212,8 +217,10 @@
// Endian conversion use well-known host to network (big endian) naming
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_LE
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_BE
#endif
#define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
@@ -239,8 +246,10 @@
// Endian conversion use well-known host to network (big endian) naming
#if defined(__LIT)
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_LE
#else
#define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #define TU_BITFIELD_ORDER TU_BITFIELD_BE
#endif
#define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16))
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 06d25d131..a8ac99fd2 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -281,14 +281,14 @@ static void hwff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uin
// Write full words to the linear part of the buffer
const uint8_t data_stride = access_mode->data_stride;
const uint32_t odd_mask = data_stride - 1;
- uint16_t lin_even = lin_bytes & ~odd_mask;
+ uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask);
tu_hwfifo_read(hwfifo, ff_buf, lin_even, access_mode);
HWFIFO_ADDR_NEXT_N(hwfifo, const, lin_even * HWFIFO_ADDR_DATA_RATIO);
ff_buf += lin_even;
// There could be an odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary
// combine it with the wrapped part to form a full word for data stride
- const uint8_t lin_odd = lin_bytes & odd_mask;
+ const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask);
if (lin_odd > 0) {
const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd);
uint8_t buf_temp[4];
@@ -338,13 +338,13 @@ static void hwff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t
// Read full words from linear part
const uint8_t data_stride = access_mode->data_stride;
const uint32_t odd_mask = data_stride - 1;
- uint16_t lin_even = lin_bytes & ~odd_mask;
+ uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask);
tu_hwfifo_write(hwfifo, ff_buf, lin_even, access_mode);
HWFIFO_ADDR_NEXT_N(hwfifo, , lin_even * HWFIFO_ADDR_DATA_RATIO);
ff_buf += lin_even;
// There could be odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary
- const uint8_t lin_odd = lin_bytes & odd_mask;
+ const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask);
if (lin_odd > 0) {
const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd);
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 77a0bbf1d..c85ade4d0 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -177,12 +177,12 @@
#elif TU_CHECK_MCU(OPT_MCU_SAMG)
#define TUP_DCD_ENDPOINT_MAX 6
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
#elif TU_CHECK_MCU(OPT_MCU_SAMX7X)
#define TUP_DCD_ENDPOINT_MAX 10
#define TUP_RHPORT_HIGHSPEED 1
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
// Enable dcache if DMA is enabled
#define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_SAMX7X_DMA_ENABLE
@@ -190,11 +190,11 @@
#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ)
#define TUP_DCD_ENDPOINT_MAX 8
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
#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 CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
#define TUP_DCD_EDPT_CLOSE_API
//--------------------------------------------------------------------+
@@ -411,7 +411,7 @@
#elif TU_CHECK_MCU(OPT_MCU_CXD56)
#define TUP_DCD_ENDPOINT_MAX 7
#define TUP_RHPORT_HIGHSPEED 1
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
//--------------------------------------------------------------------+
// TI
@@ -455,7 +455,7 @@
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
// clang-format on
- #if CFG_TUSB_MCU == OPT_MCU_ESP32S3
+ #if CFG_TUSB_MCU == OPT_MCU_ESP32S3 || CFG_TUSB_MCU == OPT_MCU_ESP32H4
#define TUP_MCU_MULTIPLE_CORE 1
#endif
@@ -476,6 +476,22 @@
#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_ESP32S31)
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_ESP32
+ #define TUP_RHPORT_HIGHSPEED 1
+ #define TUP_DCD_ENDPOINT_MAX 16
+
+ // clang-format off
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+ // clang-format on
+
+ #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
+
#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))
@@ -547,12 +563,12 @@
#elif TU_CHECK_MCU(OPT_MCU_FT90X)
#define TUP_DCD_ENDPOINT_MAX 8
#define TUP_RHPORT_HIGHSPEED 1
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
#elif TU_CHECK_MCU(OPT_MCU_FT93X)
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_RHPORT_HIGHSPEED 1
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
//--------------------------------------------------------------------+
// Allwinner
@@ -643,7 +659,7 @@
#define TUP_USBIP_MUSB_ADI
#define TUP_DCD_ENDPOINT_MAX 12
#define TUP_RHPORT_HIGHSPEED 1
- #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1
//--------------------------------------------------------------------+
// ArteryTek
@@ -727,3 +743,14 @@
#ifndef TUP_DCD_EDPT_CLOSE_API
#define TUP_DCD_EDPT_ISO_ALLOC
#endif
+
+// Some USBIPs (SAMG, SAMX7X, PIC32, MAX3266x/MAX78002) cannot assign the same endpoint
+// number to both IN and OUT. Default to 0 (same endpoint number may be used for IN and OUT).
+#ifndef CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 0
+#endif
+
+// Backward-compatible alias: legacy code only tests defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY && !defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+#endif
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index a18f9feb7..959fc129a 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -100,12 +100,14 @@ typedef enum {
} tusb_xfer_type_t;
typedef enum {
- TUSB_DIR_OUT = 0,
- TUSB_DIR_IN = 1,
+ TUSB_DIR_OUT = 0u,
+ TUSB_DIR_IN = 1u,
+} tusb_dir_t;
- TUSB_EPNUM_MASK = 0x0F,
+enum {
+ TUSB_EPNUM_MASK = 0x0F,
TUSB_DIR_IN_MASK = 0x80
-} tusb_dir_t;
+};
enum {
TUSB_EPSIZE_BULK_FS = 64,
@@ -319,6 +321,12 @@ enum {
TUSB_INDEX_INVALID_8 = 0xFF
};
+enum {
+ TU_EP0_OUT = 0x00,
+ TU_EP0_IN = 0x80
+};
+
+
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
@@ -409,10 +417,19 @@ typedef struct TU_ATTR_PACKED {
uint8_t bEndpointAddress ; // The address of the endpoint
struct TU_ATTR_PACKED {
+#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE)
uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
uint8_t usage : 2; // Data, Feedback, Implicit feedback
uint8_t : 2;
+#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE)
+ uint8_t : 2;
+ uint8_t usage : 2;
+ uint8_t sync : 2;
+ uint8_t xfer : 2;
+#else
+ #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE"
+#endif
} bmAttributes;
uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame
@@ -522,9 +539,17 @@ typedef struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
union {
struct TU_ATTR_PACKED {
+#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE)
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
+#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE)
+ uint8_t direction : 1; ///< Direction type. tusb_dir_t
+ uint8_t type : 2; ///< Request type tusb_request_type_t.
+ uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
+#else
+ #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE"
+#endif
} bmRequestType_bit;
uint8_t bmRequestType;
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 850c37bc2..f861eb258 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -219,6 +219,11 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport
event.rhport = rhport;
event.event_id = DCD_EVENT_SETUP_RECEIVED;
(void) memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
+ // USB wire format is little-endian. Convert multi-byte fields to host byte order
+ // so the stack always sees correct values regardless of CPU endianness.
+ event.setup_received.wValue = tu_le16toh(event.setup_received.wValue);
+ event.setup_received.wIndex = tu_le16toh(event.setup_received.wIndex);
+ event.setup_received.wLength = tu_le16toh(event.setup_received.wLength);
dcd_event_handler(&event, in_isr);
}
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 3c14175f6..da0ffb4c6 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -1212,7 +1212,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
TU_LOG_USBD(" String[%u]\r\n", desc_index);
// String Descriptor always uses the desc set from user
- uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, tu_le16toh(p_request->wIndex));
+ uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex);
TU_VERIFY(desc_str);
// first byte of descriptor is its size
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index 87593d4a7..49ecd0f16 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -44,10 +44,6 @@ TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_r
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum {
- EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80
-};
typedef struct {
tusb_control_request_t request;
@@ -74,7 +70,7 @@ uint8_t* usbd_get_ctrl_buf(void) {
// Queue ZLP status transaction
static inline bool status_stage_xact(uint8_t rhport, const tusb_control_request_t* request) {
// Opposite to endpoint in Data Phase
- const uint8_t ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
+ const uint8_t ep_addr = request->bmRequestType_bit.direction ? TU_EP0_OUT : TU_EP0_IN;
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0, false);
}
@@ -93,10 +89,10 @@ bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) {
// This function can also transfer an zero-length packet
static bool data_stage_xact(uint8_t rhport) {
const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE);
- uint8_t ep_addr = EDPT_CTRL_OUT;
+ uint8_t ep_addr = TU_EP0_OUT;
if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
- ep_addr = EDPT_CTRL_IN;
+ ep_addr = TU_EP0_IN;
if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) {
TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len));
}
@@ -203,8 +199,8 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
TU_ASSERT(status_stage_xact(rhport, &_ctrl_xfer.request));
} else {
// Stall both IN and OUT control endpoint
- dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
- dcd_edpt_stall(rhport, EDPT_CTRL_IN);
+ dcd_edpt_stall(rhport, TU_EP0_OUT);
+ dcd_edpt_stall(rhport, TU_EP0_IN);
}
} else {
// More data to transfer
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
index 312a98299..62df1a6d5 100644
--- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -360,7 +360,7 @@ static bool edpt_open(uint8_t rhport, uint8_t ep_addr, uint16_t 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;
- CI_REG->EP[epn].CTL |= val;
+ CI_REG->EP[epn].CTL |= (uint8_t)val;
if (xfer != TUSB_XFER_ISOCHRONOUS) {
bd[odd].dts = 1;
@@ -434,11 +434,11 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
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->bc = (total_bytes >= mps ? mps : total_bytes);
bd->addr = buffer;
__DSB();
bd->own = 1; /* This bit must be set last */
@@ -506,16 +506,16 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
void dcd_int_handler(uint8_t rhport)
{
- uint32_t is = CI_REG->INT_STAT;
- uint32_t msk = CI_REG->INT_EN;
+ uint8_t is = CI_REG->INT_STAT;
+ uint8_t msk = CI_REG->INT_EN;
// clear non-enabled interrupts
- CI_REG->INT_STAT = is & ~msk;
+ CI_REG->INT_STAT = (uint8_t)(is & ~msk);
is &= msk;
if (is & USB_ISTAT_ERROR_MASK) {
/* TODO: */
- uint32_t es = CI_REG->ERR_STAT;
+ uint8_t es = CI_REG->ERR_STAT;
CI_REG->ERR_STAT = es;
CI_REG->INT_STAT = is; /* discard any pending events */
}
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index a283c8362..7d90b1f94 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -148,8 +148,8 @@ typedef struct
#ifndef TU_DA146XX_DMA_RX_CHANNEL
#define TU_DA146XX_DMA_RX_CHANNEL 6
#endif
-#define DA146XX_DMA_USB_MUX (0x6 << (TU_DA146XX_DMA_RX_CHANNEL * 2))
-#define DA146XX_DMA_USB_MUX_MASK (0xF << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+#define DA146XX_DMA_USB_MUX (0x6u << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+#define DA146XX_DMA_USB_MUX_MASK (0xFu << (TU_DA146XX_DMA_RX_CHANNEL * 2))
typedef struct
{
@@ -311,12 +311,12 @@ static void fill_tx_fifo(xfer_ctl_t * xfer)
// Max packet size is set to value greater then FIFO. Enable fifo level warning
// to handle larger packets.
regs->txc |= (3 << USB_USB_TXC1_REG_USB_TFWL_Pos);
- USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos);
+ USB->USB_FWMSK_REG |= 1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos);
}
else
{
regs->txc &= ~USB_USB_TXC1_REG_USB_TFWL_Msk;
- USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos));
+ USB->USB_FWMSK_REG &= ~(1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos));
// Whole packet already in fifo, no need to refill it later. Mark last.
regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
}
@@ -371,14 +371,14 @@ static void start_rx_packet(xfer_ctl_t *xfer)
// For endpoint size greater than FIFO size enable FIFO level warning interrupt
// when FIFO has less than 17 bytes free.
regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk;
- USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos);
+ USB->USB_FWMSK_REG |= 1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos);
}
}
else if (epnum != 0)
{
// If max_packet_size would fit in FIFO no need for FIFO level warning interrupt.
regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk;
- USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos));
+ USB->USB_FWMSK_REG &= ~(1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos));
}
regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
}
@@ -388,7 +388,7 @@ static void start_tx_dma(void *src, volatile void *dst, uint16_t size)
// Setup SRC and DST registers
TX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src;
TX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst;
- // Interrupt not needed
+ // Interrupt is not needed
TX_DMA_REGS->DMAx_INT_REG = size;
TX_DMA_REGS->DMAx_LEN_REG = size - 1;
TX_DMA_REGS->DMAx_CTRL_REG = TX_DMA_START;
@@ -430,7 +430,9 @@ static uint16_t read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo)
uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size;
- for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd;
+ for (int i = 0; i < receive_this_time; ++i) {
+ buf[i] = (uint8_t)regs->rxd;
+ }
xfer->last_packet_size += receive_this_time;
@@ -449,7 +451,9 @@ static void handle_ep0_rx(void)
{
xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN);
// Setup packet is in
- for (int i = 0; i < fifo_bytes; ++i) _setup_packet[i] = USB->USB_RXD0_REG;
+ for (int i = 0; i < fifo_bytes; ++i) {
+ _setup_packet[i] = (uint8_t)USB->USB_RXD0_REG;
+ }
xfer->stall = 0;
xfer->data1 = 1;
@@ -469,7 +473,7 @@ static void handle_ep0_rx(void)
}
else
{
- read_rx_fifo(xfer, fifo_bytes);
+ read_rx_fifo(xfer, (uint16_t)fifo_bytes);
if (rxs0 & USB_USB_RXS0_REG_USB_RX_LAST_Msk)
{
xfer->transferred += xfer->last_packet_size;
@@ -553,7 +557,7 @@ static void handle_epx_rx_ev(uint8_t ep)
{
// Disable DMA and update last_packet_size with what DMA reported.
RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
- xfer->last_packet_size = RX_DMA_REGS->DMAx_IDX_REG;
+ xfer->last_packet_size = (uint16_t)RX_DMA_REGS->DMAx_IDX_REG;
// When DMA did not finished (packet was smaller then MPS), DMAx_IDX_REG holds exact number of bytes transmitted.
// When DMA finished value in DMAx_IDX_REG is one less then actual number of transmitted bytes.
if (xfer->last_packet_size == RX_DMA_REGS->DMAx_LEN_REG) xfer->last_packet_size++;
@@ -564,7 +568,7 @@ static void handle_epx_rx_ev(uint8_t ep)
// FIFO maybe empty if DMA read it before or it's final iteration and function already read all that was to read.
if (fifo_bytes > 0)
{
- fifo_bytes = read_rx_fifo(xfer, fifo_bytes);
+ fifo_bytes = read_rx_fifo(xfer, (uint16_t)fifo_bytes);
}
if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST))
{
@@ -624,7 +628,7 @@ static void handle_epx_tx_ev(xfer_ctl_t *xfer)
{
// Disable DMA and update last_packet_size with what DMA reported.
TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk;
- xfer->last_packet_size = TX_DMA_REGS->DMAx_IDX_REG + 1;
+ xfer->last_packet_size = (uint16_t)(TX_DMA_REGS->DMAx_IDX_REG + 1);
// Release DMA to used by other endpoints.
_dcd.dma_ep[TUSB_DIR_IN] = 0;
}
@@ -954,13 +958,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
if (dir == TUSB_DIR_OUT)
{
regs->epc_out = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
- USB->USB_RXMSK_REG |= 0x11 << (epnum - 1);
+ USB->USB_RXMSK_REG |= 0x11u << (epnum - 1);
REG_SET_BIT(USB_MAMSK_REG, USB_M_RX_EV);
}
else
{
regs->epc_in = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask;
- USB->USB_TXMSK_REG |= 0x11 << (epnum - 1);
+ USB->USB_TXMSK_REG |= 0x11u << (epnum - 1);
REG_SET_BIT(USB_MAMSK_REG, USB_M_TX_EV);
}
}
@@ -974,8 +978,8 @@ void dcd_edpt_close_all (uint8_t rhport)
for (int epnum = 1; epnum < EP_MAX; ++epnum)
{
- dcd_edpt_close(0, epnum | TUSB_DIR_OUT);
- dcd_edpt_close(0, epnum | TUSB_DIR_IN);
+ dcd_edpt_close(0, (uint8_t)(epnum | TUSB_DIR_OUT));
+ dcd_edpt_close(0, (uint8_t)(epnum | TUSB_DIR_IN));
}
}
@@ -1001,7 +1005,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
regs->rxc = USB_USB_RXC1_REG_USB_FLUSH_Msk;
regs->epc_out = 0;
- USB->USB_RXMSK_REG &= ~(0x11 << (epnum - 1));
+ USB->USB_RXMSK_REG &= ~(0x11u << (epnum - 1));
// Release DMA if needed
if (_dcd.dma_ep[TUSB_DIR_OUT] == epnum)
{
@@ -1013,7 +1017,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk;
regs->epc_in = 0;
- USB->USB_TXMSK_REG &= ~(0x11 << (epnum - 1));
+ USB->USB_TXMSK_REG &= ~(0x11u << (epnum - 1));
// Release DMA if needed
if (_dcd.dma_ep[TUSB_DIR_IN] == epnum)
{
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c
index 339048473..56429ac1f 100644
--- a/src/portable/mentor/musb/dcd_musb.c
+++ b/src/portable/mentor/musb/dcd_musb.c
@@ -50,36 +50,70 @@
* MACRO TYPEDEF CONSTANT ENUM DECLARATION
*------------------------------------------------------------------*/
-#define REQUEST_TYPE_INVALID (0xFFu)
-
typedef union {
volatile uint8_t u8;
volatile uint16_t u16;
volatile uint32_t u32;
} hw_fifo_t;
-typedef struct TU_ATTR_PACKED
-{
- void *buf; /* the start address of a transfer data buffer */
+typedef struct {
+ union {
+ uint8_t *buf; /* the start address of a transfer data buffer */
+ tu_fifo_t *fifo;
+ };
uint16_t length; /* the number of bytes in the buffer */
uint16_t remaining; /* the number of bytes remaining in the buffer */
+ bool armed; /* true while a transfer is posted */
+ bool use_fifo; /* true: buf is tu_fifo_t*; false: buf is plain byte pointer. */
} pipe_state_t;
-typedef struct
-{
- union {
- tusb_control_request_t setup_packet;
- uint32_t setup_buffer[2];
- };
- uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
- int8_t status_out;
- pipe_state_t pipe0;
- pipe_state_t pipe[2][TUP_DCD_ENDPOINT_MAX-1]; /* pipe[direction][endpoint number - 1] */
- uint16_t pipe_buf_is_fifo[2]; /* Bitmap. Each bit means whether 1:TU_FIFO or 0:POD. */
+// Pipe array layout (N = TUP_DCD_ENDPOINT_MAX). EP0 has its own scalars in
+// dcd_data_t and does not occupy a pipe slot.
+// One-direction-only IPs (CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY=1):
+// [0..N-2] : EP1..N-1 (single slot per endpoint)
+// Bidirectional-capable IPs:
+// [0..N-2 ] : EP1..N-1 OUT
+// [N-1..2*N-3 ] : EP1..N-1 IN
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #define MUSB_PIPE_COUNT (TUP_DCD_ENDPOINT_MAX - 1u)
+#else
+ #define MUSB_PIPE_COUNT (2u * (TUP_DCD_ENDPOINT_MAX - 1u))
+#endif
+
+enum {
+ PIPE0_STATE_IDLE = 0, // no active control transfer
+ PIPE0_STATE_DATA, // DATA stage (IN or OUT — direction implied by CSR/dir)
+ PIPE0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP; awaits send-ACK IRQ
+ PIPE0_STATE_STATUS_OUT, // post-DATAEND, neither edpt0_xfer(STATUS OUT) nor confirmation IRQ has happened yet
+ PIPE0_STATE_STATUS_OUT_PENDING, // one of {edpt0_xfer(STATUS OUT), confirmation IRQ} has happened; the other fires xfer_complete
+};
+
+typedef struct {
+ struct {
+ uint8_t *buf; // DATA OUT drain target (only valid while EP0 is in DATA OUT stage)
+ uint16_t xact_len; // chunk length most recently armed via edpt0_xfer; reported in xfer_complete
+ uint16_t remain_wlength; // bytes remaining in the control transfer's DATA stage
+ uint8_t state;
+ uint8_t pending_addr; // new USB address latched by dcd_set_address; applied when STATUS IN completes
+ } pipe0;
+ pipe_state_t pipe[MUSB_PIPE_COUNT];
} dcd_data_t;
static dcd_data_t _dcd;
+// EP0 must not call this — it has its own scalars in dcd_data_t.
+TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_dir_t epdir) {
+ size_t idx = epnum - 1u;
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ (void) epdir;
+#else
+ if (epdir == TUSB_DIR_IN) {
+ idx += TUP_DCD_ENDPOINT_MAX - 1u;
+ }
+#endif
+ return &_dcd.pipe[idx];
+}
+
//--------------------------------------------------------------------
// HW FIFO Helper
// Note: Index register is already set by caller
@@ -110,7 +144,6 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne
TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps,
bool double_packet) {
- (void) epnum;
uint8_t ffsize = hwfifo_byte2size(mps);
mps = 8 << ffsize; // round up to the next power of 2
@@ -123,6 +156,13 @@ TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsign
musb->fifo_addr[is_rx] = alloced_fifo_bytes / 8;
musb->fifo_size[is_rx] = ffsize;
+ volatile uint16_t* dp_disable = is_rx ? &musb->rx_doulbe_packet_disable : &musb->tx_double_packet_disable;
+ if (double_packet) {
+ *dp_disable &= ~(1u << epnum);
+ } else {
+ *dp_disable |= (1u << epnum);
+ }
+
alloced_fifo_bytes += mps;
return true;
}
@@ -136,18 +176,29 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne
TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps,
bool double_packet) {
- (void) epnum; (void) mps;
- if (!double_packet) {
- #if defined(TUP_USBIP_MUSB_ADI)
- musb->indexed_csr.maxp_csr[is_rx].csrh |= MUSB_CSRH_DISABLE_DOUBLE_PACKET(is_rx);
- #else
- if (is_rx) {
- musb->rx_doulbe_packet_disable |= 1u << epnum;
- } else {
- musb->tx_double_packet_disable |= 1u << epnum;
- }
- #endif
+ (void) mps;
+
+ #if defined(TUP_USBIP_MUSB_ADI)
+ // AnalogDevice FIFO sizes: EP1..7 = 512 B, EP8..9 = 2048 B, EP10..11 = 4096 B.
+ // DPB requires FIFO >= 2 * MPS. For HS bulk (MPS=512) only EP >= 8 qualifies.
+ // Force single-buffered on EP < 8 even if the caller requested DPB.
+ if (epnum < 8 && (musb->power & MUSB_POWER_HSMODE)) {
+ double_packet = false;
+ }
+ volatile uint8_t* csrh = &musb->indexed_csr.maxp_csr[is_rx].csrh;
+ if (double_packet) {
+ *csrh &= ~MUSB_CSRH_DISABLE_DOUBLE_PACKET;
+ } else {
+ *csrh |= MUSB_CSRH_DISABLE_DOUBLE_PACKET;
+ }
+ #else
+ volatile uint16_t* dp_disable = is_rx ? &musb->rx_doulbe_packet_disable : &musb->tx_double_packet_disable;
+ if (double_packet) {
+ *dp_disable &= ~(1u << epnum);
+ } else {
+ *dp_disable |= (1u << epnum);
}
+ #endif
return true;
}
@@ -167,298 +218,299 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigne
}
}
-static void process_setup_packet(uint8_t rhport) {
- musb_regs_t* musb_regs = MUSB_REGS(rhport);
-
- // Read setup packet
- _dcd.setup_buffer[0] = musb_regs->fifo[0];
- _dcd.setup_buffer[1] = musb_regs->fifo[0];
-
- _dcd.pipe0.buf = NULL;
- _dcd.pipe0.length = 0;
- _dcd.pipe0.remaining = 0;
- dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true);
-
- const unsigned len = _dcd.setup_packet.wLength;
- _dcd.remaining_ctrl = len;
- const unsigned dir_in = tu_edpt_dir(_dcd.setup_packet.bmRequestType);
- /* Clear RX FIFO and reverse the transaction direction */
- if (len && dir_in) {
- musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
- ep_csr->csr0l = MUSB_CSRL0_RXRDYC;
+// write to txfifo using pipe_state_t info
+static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) {
+ musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr;
+ const uint16_t mps = ep_csr->tx_maxp & MUSB_TXMAXP_PACKET_SIZE_M;
+ const uint16_t xact_len = tu_min16(mps, pipe->remaining);
+ volatile void *hwfifo = &musb_regs->fifo[epnum];
+ if (xact_len) {
+ if (pipe->use_fifo) {
+ tu_hwfifo_write_from_fifo(hwfifo, pipe->fifo, xact_len, NULL);
+ } else {
+ tu_hwfifo_write(hwfifo, pipe->buf, xact_len, NULL);
+ pipe->buf += xact_len;
+ }
+ pipe->remaining -= xact_len;
}
+ ep_csr->tx_csrl = MUSB_TXCSRL1_TXRDY;
}
-static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) {
- unsigned epnum = tu_edpt_number(ep_addr);
- unsigned epnum_minus1 = epnum - 1;
- pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
- const unsigned rem = pipe->remaining;
+// Called from the TX interrupt. If the last queued packet finished the transfer,
+// signal completion; otherwise queue the next packet.
+static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) {
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
+ const uint_fast8_t csrl = ep_csr->tx_csrl;
+ if (csrl & MUSB_TXCSRL1_STALLED) {
+ ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN);
+ return; // sent STALL, do nothing
+ }
- if (rem == 0 && pipe->length > 0) {
+ pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN);
+ if (pipe->remaining > 0) {
+ pipe_write(musb_regs, pipe, epnum);
+ } else {
+ // All bytes have been loaded into the FIFO. With double-packet buffering a
+ // second packet may still be waiting in the FIFO when this IRQ fires (the
+ // hardware signals TXRDY clear as soon as a slot frees, not when the wire
+ // transfer finishes). Defer completion until FIFONE == 0 so we don't emit
+ // a duplicate xfer_complete before the final packet has been sent.
+ if (csrl & MUSB_TXCSRL1_FIFONE) {
+ return;
+ }
+ const uint16_t xferred_len = pipe->length;
pipe->buf = NULL;
- return true;
+ pipe->armed = false;
+ dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_IN), xferred_len, XFER_RESULT_SUCCESS, true);
}
+}
- musb_regs_t* musb_regs = MUSB_REGS(rhport);
- musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
- const unsigned mps = ep_csr->tx_maxp;
- const unsigned len = TU_MIN(mps, rem);
- void *buf = pipe->buf;
- volatile void *fifo_ptr = &musb_regs->fifo[epnum];
- // TU_LOG1(" %p mps %d len %d rem %d\r\n", buf, mps, len, rem);
- if (len) {
- if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
- tu_hwfifo_write_from_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL);
+// Drain one packet from the Rx FIFO into pipe->buf/fifo, update pipe state, and
+// release the FIFO slot by clearing RXRDY. return true if short packet
+static bool pipe_read(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) {
+ musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr; // index already set in process_epout()
+ const uint16_t mps = ep_csr->rx_maxp & MUSB_RXMAXP_PACKET_SIZE_M;
+ const uint16_t rx_count = ep_csr->rx_count;
+ const uint16_t xact_len = tu_min16(tu_min16(pipe->remaining, mps), rx_count);
+ volatile void *hwfifo = &musb_regs->fifo[epnum];
+ if (xact_len) {
+ if (pipe->use_fifo) {
+ tu_hwfifo_read_to_fifo(hwfifo, pipe->fifo, xact_len, NULL);
} else {
- tu_hwfifo_write(fifo_ptr, buf, len, NULL);
- pipe->buf = (uint8_t*)buf + len;
+ tu_hwfifo_read(hwfifo, pipe->buf, xact_len, NULL);
+ pipe->buf += xact_len;
}
- pipe->remaining = rem - len;
+ pipe->remaining -= xact_len;
}
- ep_csr->tx_csrl = MUSB_TXCSRL1_TXRDY;
- // TU_LOG1(" TXCSRL%d = %x %d\r\n", epnum, ep_csr->tx_csrl, rem - len);
- return false;
+ ep_csr->rx_csrl = 0; /* Clear RXRDY - release this FIFO slot */
+
+ return (xact_len < mps);
}
-static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr)
-{
- unsigned epnum = tu_edpt_number(ep_addr);
- unsigned epnum_minus1 = epnum - 1;
- pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
- musb_regs_t* musb_regs = MUSB_REGS(rhport);
+static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum, bool is_isr) {
musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
- // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, ep_csr->rx_csrl);
+ if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) {
+ ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER);
+ return; // sent STALL, do nothing
+ }
- TU_ASSERT(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY);
+ // Fail gracefully. Spurious interrupt.
+ if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) {
+ return;
+ }
- const unsigned mps = ep_csr->rx_maxp;
- const unsigned rem = pipe->remaining;
- const unsigned vld = ep_csr->rx_count;
- const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
- void *buf = pipe->buf;
- volatile void *fifo_ptr = &musb_regs->fifo[epnum];
- if (len) {
- if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
- tu_hwfifo_read_to_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL);
- } else {
- tu_hwfifo_read(fifo_ptr, buf, len, NULL);
- pipe->buf = (uint8_t*)buf + len;
- }
- pipe->remaining = rem - len;
+ pipe_state_t *pipe = pipe_get(epnum, TUSB_DIR_OUT);
+ if (!pipe->armed) {
+ // Packet is already ACK'd by hardware and sitting in the Rx FIFO, but no transfer is
+ // posted. Do NOT flush (per MUSB spec §3.3.11 FlushFIFO) - that would silently drop
+ // acknowledged data. Mask this endpoint's Rx interrupt so the ISR stops re-firing;
+ // the FIFO stays occupied so hardware NAKs further OUT tokens (natural backpressure).
+ // The next dcd_edpt_xfer() on this endpoint will drain the staged packet.
+ musb_regs->intr_rxen &= (uint16_t) ~TU_BIT(epnum);
+ return;
}
- if ((len < mps) || (rem == len)) {
+
+ const bool is_short = pipe_read(musb_regs, pipe, epnum);
+
+ // Transfer completes on a short packet or when the rx buffer is filled.
+ if (is_short || pipe->remaining == 0) {
+ const uint16_t xferred_len = pipe->length - pipe->remaining;
pipe->buf = NULL;
- return NULL != buf;
+ pipe->armed = false;
+ dcd_event_xfer_complete(rhport, epnum, xferred_len, XFER_RESULT_SUCCESS, is_isr);
}
- ep_csr->rx_csrl = 0; /* Clear RXRDY bit */
- return false;
}
-static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
-{
- unsigned epnum = tu_edpt_number(ep_addr);
- unsigned epnum_minus1 = epnum - 1;
- unsigned dir_in = tu_edpt_dir(ep_addr);
+static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo, bool is_isr) {
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
- pipe_state_t *pipe = &_dcd.pipe[dir_in][epnum_minus1];
- pipe->buf = buffer;
- pipe->length = total_bytes;
- pipe->remaining = total_bytes;
+ pipe_state_t *pipe = pipe_get(epnum, dir_in);
+ if (use_fifo) {
+ pipe->fifo = (tu_fifo_t *)buffer;
+ } else {
+ pipe->buf = (uint8_t *)buffer;
+ }
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
+ pipe->use_fifo = use_fifo;
+ pipe->armed = true;
+
+ musb_regs_t *musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t *ep_csr = get_ep_csr(musb_regs, epnum);
if (dir_in) {
- handle_xfer_in(rhport, ep_addr);
+ pipe_write(musb_regs, pipe, epnum);
} else {
- musb_regs_t* musb_regs = MUSB_REGS(rhport);
- musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
- if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) ep_csr->rx_csrl = 0;
+ // Re-enable Rx interrupt (may have been masked by the no-buffer path in process_epout)
+ musb_regs->intr_rxen |= (uint16_t)TU_BIT(epnum);
+
+ // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt.
+ // process_epout() fires dcd_event_xfer_complete() itself if the drain completes.
+ if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) {
+ process_epout(rhport, musb_regs, epnum, is_isr);
+ }
}
return true;
}
-static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
-{
- (void)rhport;
- TU_ASSERT(total_bytes <= 64); /* Current implementation supports for only up to 64 bytes. */
+static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
+ TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); /* EP0 only supports 1 packet per dcd_edpt_xfer()*/
musb_regs_t* musb_regs = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
- const unsigned req = _dcd.setup_packet.bmRequestType;
- TU_ASSERT(req != REQUEST_TYPE_INVALID || total_bytes == 0);
-
- if (req == REQUEST_TYPE_INVALID || _dcd.status_out) {
- /* STATUS OUT stage.
- * MUSB controller automatically handles STATUS OUT packets without
- * software helps. We do not have to do anything. And STATUS stage
- * may have already finished and received the next setup packet
- * without calling this function, so we have no choice but to
- * invoke the callback function of status packet here. */
- // TU_LOG1(" STATUS OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
- _dcd.status_out = 0;
- if (req == REQUEST_TYPE_INVALID) {
- dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
- } else {
- /* The next setup packet has already been received, it aborts
- * invoking callback function to avoid confusing TUSB stack. */
- TU_LOG1("Drop CONTROL_STAGE_ACK\r\n");
- }
- return true;
- }
const unsigned dir_in = tu_edpt_dir(ep_addr);
- if (tu_edpt_dir(req) == dir_in) { /* DATA stage */
- TU_ASSERT(total_bytes <= _dcd.remaining_ctrl);
- const unsigned rem = _dcd.remaining_ctrl;
- const unsigned len = TU_MIN(TU_MIN(rem, 64), total_bytes);
- volatile void *fifo_ptr = &musb_regs->fifo[0];
- if (dir_in) {
- tu_hwfifo_write(fifo_ptr, buffer, len, NULL);
- _dcd.pipe0.buf = buffer + len;
- _dcd.pipe0.length = len;
- _dcd.pipe0.remaining = 0;
-
- _dcd.remaining_ctrl = rem - len;
- if ((len < 64) || (rem == len)) {
- _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; /* Change to STATUS/SETUP stage */
- _dcd.status_out = 1;
- /* Flush TX FIFO and reverse the transaction direction. */
- ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND;
+ switch (_dcd.pipe0.state) {
+ case PIPE0_STATE_DATA: {
+ _dcd.pipe0.xact_len = total_bytes;
+ if (dir_in) {
+ // DATA IN: load FIFO, set TXRDY. Add DATAEND on the last chunk
+ // (ep0_remain_datalen == 0 after this load) to end the data stage.
+ tu_hwfifo_write(&musb_regs->fifo[0], buffer, total_bytes, NULL);
+ _dcd.pipe0.remain_wlength -= total_bytes;
+ if (_dcd.pipe0.remain_wlength == 0) {
+ ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND;
+ } else {
+ ep_csr->csr0l = MUSB_CSRL0_TXRDY;
+ }
} else {
- ep_csr->csr0l = MUSB_CSRL0_TXRDY; /* Flush TX FIFO to return ACK. */
+ // DATA OUT: arm drain target, ack RXRDY so host can send DATA OUT.
+ _dcd.pipe0.buf = buffer;
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC;
}
- // TU_LOG1(" IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
- } else {
- // TU_LOG1(" OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
- _dcd.pipe0.buf = buffer;
- _dcd.pipe0.length = len;
- _dcd.pipe0.remaining = len;
- ep_csr->csr0l = MUSB_CSRL0_RXRDYC; /* Clear RX FIFO to return ACK. */
+ break;
}
- } else if (dir_in) {
- // TU_LOG1(" STATUS IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
- _dcd.pipe0.buf = NULL;
- _dcd.pipe0.length = 0;
- _dcd.pipe0.remaining = 0;
- /* Clear RX FIFO and reverse the transaction direction */
- ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND;
+
+ case PIPE0_STATE_STATUS_IN:
+ TU_ASSERT(dir_in && total_bytes == 0); // only STATUS IN allowed
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND;
+ break;
+
+ case PIPE0_STATE_STATUS_OUT:
+ TU_ASSERT(!dir_in && total_bytes == 0); // only STATUS OUT allowed
+ // First event of the STATUS OUT pair — wait for the IRQ to fire complete.
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT_PENDING;
+ break;
+
+ case PIPE0_STATE_STATUS_OUT_PENDING:
+ // Second event — IRQ already arrived, fire complete now.
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
+ dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, is_isr);
+ break;
+
+ default: break;
}
+
return true;
}
-static void process_ep0(uint8_t rhport)
-{
+// 21.1.5: endpoint 0 service routine as peripheral
+static void process_ep0(uint8_t rhport) {
musb_regs_t* musb_regs = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
uint_fast8_t csrl = ep_csr->csr0l;
- // TU_LOG1(" EP0 ep_csr->csr0l = %x\r\n", csrl);
- // 21.1.5: endpoint 0 service routine as peripheral
-
if (csrl & MUSB_CSRL0_STALLED) {
- /* Returned STALL packet to HOST. */
- ep_csr->csr0l = 0; /* Clear STALL */
+ ep_csr->csr0l = 0;
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
return;
}
- unsigned req = _dcd.setup_packet.bmRequestType;
if (csrl & MUSB_CSRL0_SETEND) {
- TU_LOG1(" ABORT by the next packets\r\n");
+ // Host aborted the current control transfer (new SETUP or premature STATUS).
+ // do nothing, it is probably another setup packet, usbd will reset its state.
ep_csr->csr0l = MUSB_CSRL0_SETENDC;
- if (req != REQUEST_TYPE_INVALID && _dcd.pipe0.buf) {
- /* DATA stage was aborted by receiving STATUS or SETUP packet. */
- _dcd.pipe0.buf = NULL;
- _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
- dcd_event_xfer_complete(rhport,
- req & TUSB_DIR_IN_MASK,
- _dcd.pipe0.length - _dcd.pipe0.remaining,
- XFER_RESULT_SUCCESS, true);
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
+ if (!(csrl & MUSB_CSRL0_RXRDY)) {
+ return; /* no SETUP waiting behind it */
}
- req = REQUEST_TYPE_INVALID;
- if (!(csrl & MUSB_CSRL0_RXRDY)) return; /* Received SETUP packet */
}
+ // Receive Data (Setup or OUT)
if (csrl & MUSB_CSRL0_RXRDY) {
- /* Received SETUP or DATA OUT packet */
- if (req == REQUEST_TYPE_INVALID) {
- /* SETUP */
- TU_ASSERT(sizeof(tusb_control_request_t) == ep_csr->count0,);
- process_setup_packet(rhport);
- return;
- }
- if (_dcd.pipe0.buf) {
- /* DATA OUT */
- const unsigned vld = ep_csr->count0;
- const unsigned rem = _dcd.pipe0.remaining;
- const unsigned len = TU_MIN(TU_MIN(rem, 64), vld);
- volatile void *fifo_ptr = &musb_regs->fifo[0];
- tu_hwfifo_read(fifo_ptr, _dcd.pipe0.buf, len, NULL);
+ const uint16_t count0 = ep_csr->count0;
+ switch (_dcd.pipe0.state) {
+ case PIPE0_STATE_IDLE:
+ TU_ASSERT(sizeof(tusb_control_request_t) == count0, );
+ union {
+ tusb_control_request_t req;
+ uint32_t u32[2];
+ } setup_packet;
+ setup_packet.u32[0] = musb_regs->fifo[0];
+ setup_packet.u32[1] = musb_regs->fifo[0];
- _dcd.pipe0.remaining = rem - len;
- _dcd.remaining_ctrl -= len;
+ _dcd.pipe0.remain_wlength = setup_packet.req.wLength;
- _dcd.pipe0.buf = NULL;
- dcd_event_xfer_complete(rhport,
- tu_edpt_addr(0, TUSB_DIR_OUT),
- _dcd.pipe0.length - _dcd.pipe0.remaining,
- XFER_RESULT_SUCCESS, true);
- }
- return;
- }
+ if (setup_packet.req.wLength == 0) {
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_IN;
+ } else {
+ _dcd.pipe0.state = PIPE0_STATE_DATA;
+ // If OUT (rx) direction, let edpt0_xfer() clear RXRDY when it's ready to receive data.
+ if (setup_packet.req.bmRequestType & TUSB_DIR_IN_MASK) {
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC;
+ }
+ }
+ dcd_event_setup_received(rhport, (const uint8_t *)&setup_packet.req, true);
+ break;
- /* When CSRL0 is zero, it means that completion of sending a any length packet
- * or receiving a zero length packet. */
- if (req != REQUEST_TYPE_INVALID && !tu_edpt_dir(req)) {
- /* STATUS IN */
- if (*(const uint16_t*)(uintptr_t)&_dcd.setup_packet == 0x0500) {
- /* The address must be changed on completion of the control transfer. */
- musb_regs->faddr = (uint8_t)_dcd.setup_packet.wValue;
+ case PIPE0_STATE_DATA: {
+ // EP0 OUT is single-packet (TU_ASSERT total_bytes <= EP0_SIZE in edpt0_xfer)
+ // so the whole packet drains in one shot.
+ if (count0) {
+ tu_hwfifo_read(&musb_regs->fifo[0], _dcd.pipe0.buf, count0, NULL);
+ _dcd.pipe0.remain_wlength -= count0;
+ }
+ if (_dcd.pipe0.remain_wlength == 0) {
+ // last packet: change state and leave RXRDY for edpt0_xfer(STATUS IN) to ack
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_IN;
+ } else {
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC;
+ }
+ dcd_event_xfer_complete(rhport, TU_EP0_OUT, count0, XFER_RESULT_SUCCESS, true);
+ break;
+ }
+
+ default: break;
}
- _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
- dcd_event_xfer_complete(rhport,
- tu_edpt_addr(0, TUSB_DIR_IN),
- _dcd.pipe0.length - _dcd.pipe0.remaining,
- XFER_RESULT_SUCCESS, true);
+
return;
}
- if (_dcd.pipe0.buf) {
- /* DATA IN */
- _dcd.pipe0.buf = NULL;
- dcd_event_xfer_complete(rhport,
- tu_edpt_addr(0, TUSB_DIR_IN),
- _dcd.pipe0.length - _dcd.pipe0.remaining,
- XFER_RESULT_SUCCESS, true);
- }
-}
-static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr)
-{
- bool completed;
- const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned epn_minus1 = epn - 1;
+ /* When CSRL0 is zero, it means that either
+ * - completion of sending any length packet TxPktRdy clear
+ * - or status stage is complete (ZLP) after DataEnd is set */
+ switch (_dcd.pipe0.state) {
+ case PIPE0_STATE_DATA:
+ // csrl == 0 in DATA state = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the just-sent
+ // packet was the last (DATAEND was set when ep0_remain_datalen hit zero), transition
+ // to STATUS_OUT to await the host's STATUS-OUT ZLP confirmation IRQ.
+ if (_dcd.pipe0.remain_wlength == 0) {
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT;
+ }
+ dcd_event_xfer_complete(rhport, TU_EP0_IN, _dcd.pipe0.xact_len, XFER_RESULT_SUCCESS, true);
+ break;
- musb_regs_t* musb_regs = MUSB_REGS(rhport);
- musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn);
- if (dir_in) {
- // TU_LOG1(" TX CSRL%d = %x\r\n", epn, ep_csr->tx_csrl);
- if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) {
- ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN);
- return;
- }
- completed = handle_xfer_in(rhport, ep_addr);
- } else {
- // TU_LOG1(" RX CSRL%d = %x\r\n", epn, ep_csr->rx_csrl);
- if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) {
- ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER);
- return;
- }
- completed = handle_xfer_out(rhport, ep_addr);
- }
+ case PIPE0_STATE_STATUS_OUT:
+ // First event of the STATUS OUT pair — wait for edpt0_xfer(STATUS OUT) to fire complete.
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT_PENDING;
+ break;
+
+ case PIPE0_STATE_STATUS_OUT_PENDING:
+ // Second event — edpt0_xfer(STATUS OUT) already called, fire complete now.
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
+ dcd_event_xfer_complete(rhport, TU_EP0_OUT, 0, XFER_RESULT_SUCCESS, true);
+ break;
- if (completed) {
- pipe_state_t *pipe = &_dcd.pipe[dir_in][epn_minus1];
- dcd_event_xfer_complete(rhport, ep_addr,
- pipe->length - pipe->remaining,
- XFER_RESULT_SUCCESS, true);
+ case PIPE0_STATE_STATUS_IN:
+ if (_dcd.pipe0.pending_addr) {
+ musb_regs->faddr = _dcd.pipe0.pending_addr;
+ _dcd.pipe0.pending_addr = 0;
+ }
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
+ dcd_event_xfer_complete(rhport, TU_EP0_IN, 0, XFER_RESULT_SUCCESS, true);
+ break;
+
+ default: break;
}
}
@@ -471,11 +523,10 @@ static void process_bus_reset(uint8_t rhport) {
alloced_fifo_bytes = CFG_TUD_ENDPOINT0_SIZE;
#endif
- /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF), a control transfer state is SETUP or STATUS stage. */
- _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
- _dcd.status_out = 0;
- /* When pipe0.buf has not NULL, DATA stage works in progress. */
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
_dcd.pipe0.buf = NULL;
+ _dcd.pipe0.xact_len = 0;
+ _dcd.pipe0.remain_wlength = 0;
musb->intr_txen = 1; /* Enable only EP0 */
musb->intr_rxen = 0;
@@ -537,17 +588,19 @@ void dcd_int_disable(uint8_t rhport) {
musb_dcd_int_disable(rhport);
}
-// Receive Set Address request, mcu port must also include status IN response
+// Receive Set Address request. Stash the new address here; hardware faddr is
+// latched from pending_addr in process_ep0 once the STATUS IN completes (per
+// USB spec, address must only take effect after the status stage).
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
- (void)dev_addr;
musb_regs_t* musb_regs = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
- _dcd.pipe0.buf = NULL;
- _dcd.pipe0.length = 0;
- _dcd.pipe0.remaining = 0;
- /* Clear RX FIFO to return ACK. */
+ _dcd.pipe0.pending_addr = dev_addr;
+ _dcd.pipe0.buf = NULL;
+ _dcd.pipe0.xact_len = 0;
+ _dcd.pipe0.state = PIPE0_STATE_STATUS_IN;
+ /* Send STATUS IN ZLP with DATAEND; host ACK fires the confirmation IRQ. */
ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND;
}
@@ -588,39 +641,36 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-// static void edpt_setup(musb_regs_t* musb, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_size){
-// const unsigned epn = tu_edpt_number(ep_addr);
-// const unsigned dir_in = tu_edpt_dir(ep_addr);
-// }
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
const unsigned ep_addr = ep_desc->bEndpointAddress;
const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned epdir = tu_edpt_dir(ep_addr);
const unsigned mps = tu_edpt_packet_size(ep_desc);
- pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1];
+ pipe_state_t *pipe = pipe_get(epn, epdir);
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
+ pipe->armed = false;
musb_regs_t* musb = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
- const uint8_t is_rx = 1 - dir_in;
+ const uint8_t is_rx = (1 - epdir);
musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx];
maxp_csr->maxp = mps;
maxp_csr->csrh = 0;
#if MUSB_CFG_SHARED_FIFO
- if (dir_in) {
+ if (epdir) {
maxp_csr->csrh |= MUSB_CSRH_TX_MODE;
}
#endif
hwfifo_flush(musb, epn, is_rx, true);
- TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, false));
+ TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, ep_desc->bmAttributes.xfer == TUSB_XFER_BULK));
musb->intren_ep[is_rx] |= TU_BIT(epn);
return true;
@@ -645,10 +695,11 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc )
unsigned const ie = musb_dcd_get_int_enable(rhport);
musb_dcd_int_disable(rhport);
- pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1];
+ pipe_state_t *pipe = pipe_get(epn, dir_in);
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
+ pipe->armed = false;
musb_regs_t* musb = MUSB_REGS(rhport);
musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
@@ -706,22 +757,22 @@ void dcd_edpt_close_all(uint8_t rhport)
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr)
{
- (void) is_isr;
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
unsigned const ie = musb_dcd_get_int_enable(rhport);
musb_dcd_int_disable(rhport);
if (epnum) {
- _dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] &= ~TU_BIT(epnum - 1);
- ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes);
+ ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes, false, is_isr);
} else {
- ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes);
+ (void) is_isr;
+ ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes, is_isr);
}
- if (ie) musb_dcd_int_enable(rhport);
+ if (ie) {
+ musb_dcd_int_enable(rhport);
+ }
return ret;
}
@@ -729,16 +780,13 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
// - optional, however, must be listed in usbd.c
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes, bool is_isr)
{
- (void) is_isr;
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
TU_ASSERT(epnum);
unsigned const ie = musb_dcd_get_int_enable(rhport);
musb_dcd_int_disable(rhport);
- _dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] |= TU_BIT(epnum - 1);
- ret = edpt_n_xfer(rhport, ep_addr, (uint8_t*)ff, total_bytes);
+ ret = edpt_n_xfer(rhport, ep_addr, ff, total_bytes, true, is_isr);
if (ie) musb_dcd_int_enable(rhport);
return ret;
}
@@ -753,14 +801,17 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn);
if (0 == epn) {
- if (!ep_addr) { /* Ignore EP80 */
- _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
+ if (ep_addr == TU_EP0_OUT) { /* Ignore EP0 OUT */
+ _dcd.pipe0.state = PIPE0_STATE_IDLE;
_dcd.pipe0.buf = NULL;
ep_csr->csr0l = MUSB_CSRL0_STALL;
}
} else {
- const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr);
+ const tusb_dir_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t is_rx = (ep_dir == TUSB_DIR_OUT ? 1u : 0u);
ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_SEND_STALL(is_rx);
+ pipe_state_t* pipe = pipe_get(epn, ep_dir);
+ pipe->armed = false;
}
if (ie) musb_dcd_int_enable(rhport);
@@ -815,21 +866,35 @@ void dcd_int_handler(uint8_t rhport) {
}
intr_tx &= musb_regs->intr_txen; /* Clear disabled interrupts */
- if (intr_tx & TU_BIT(0)) {
- process_ep0(rhport);
- intr_tx &= ~TU_BIT(0);
- }
+
while (intr_tx) {
- unsigned const num = __builtin_ctz(intr_tx);
- process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_IN));
- intr_tx &= ~TU_BIT(num);
+ const unsigned epnum = __builtin_ctz(intr_tx);
+ if (epnum == 0) {
+ process_ep0(rhport); // EP0 has its own state machine (control transfers)
+ } else {
+ process_epin(rhport, musb_regs, epnum);
+ }
+ intr_tx &= ~TU_BIT(epnum);
+
+ // Double packet endpoint: TxPktRdy is clear, and interrupt is generated immediately when 1st packet is written.
+ // Also catches EP0 SETUP arriving during bulk processing.
+ uint_fast8_t new_intr_tx = musb_regs->intr_tx;
+ new_intr_tx &= musb_regs->intr_txen;
+
+ intr_tx |= new_intr_tx;
}
intr_rx &= musb_regs->intr_rxen; /* Clear disabled interrupts */
while (intr_rx) {
- unsigned const num = __builtin_ctz(intr_rx);
- process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_OUT));
- intr_rx &= ~TU_BIT(num);
+ unsigned const epnum = __builtin_ctz(intr_rx);
+ process_epout(rhport, musb_regs, epnum, true);
+ intr_rx &= ~TU_BIT(epnum);
+
+ // Double packet endpoint: RxPktRdy is set and interrupt is generated immediately if 2nd packet is received
+ uint_fast8_t new_intr_rx = musb_regs->intr_rx;
+ new_intr_rx &= musb_regs->intr_rxen;
+
+ intr_rx |= new_intr_rx;
}
musb_regs->index = saved_index; // restore endpoint index
diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h
index b2f6492fa..3d3c3c834 100644
--- a/src/portable/mentor/musb/musb_type.h
+++ b/src/portable/mentor/musb/musb_type.h
@@ -300,7 +300,7 @@ TU_VERIFY_STATIC(sizeof(musb_regs_t) == 0x350, "size is not correct");
// Helper
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_regs, unsigned epnum) {
- musb_regs->index = epnum;
+ musb_regs->index = (uint8_t)epnum;
return &musb_regs->indexed_csr;
}
@@ -336,7 +336,7 @@ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_
#define MUSB_CSRL_CLEAR_DATA_TOGGLE(_rx) (1u << ((_rx) ? 7 : 6))
// 0x13, 0x17: TX/RX CSRH
-#define MUSB_CSRH_DISABLE_DOUBLE_PACKET(_rx) (1u << 1)
+#define MUSB_CSRH_DISABLE_DOUBLE_PACKET (1u << 1)
#define MUSB_CSRH_TX_MODE (1u << 5) // 1 = TX, 0 = RX. only relevant for SHARED FIFO
#define MUSB_CSRH_ISO (1u << 6)
@@ -568,6 +568,16 @@ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_
//*****************************************************************************
//
+// The following are defines for the bit fields in the MUSB_O_TXMAXP / MUSB_O_RXMAXP
+// registers. Bits [10:0] carry the maximum packet size; bits [15:11] carry
+// numpackminus1 (HB-iso / HS-bulk multiplier - 1).
+//
+//*****************************************************************************
+#define MUSB_TXMAXP_PACKET_SIZE_M 0x07FFu
+#define MUSB_RXMAXP_PACKET_SIZE_M 0x07FFu
+
+//*****************************************************************************
+//
// The following are defines for the bit fields in the MUSB_O_TXCSRL1 register.
//
//*****************************************************************************
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index 4115eecc5..f8980b775 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -352,8 +352,8 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
csr_clear(epnum, UDP_CSR_FORCESTALL_Msk);
// must also reset EP to clear data toggle
- UDP->UDP_RST_EP |= (1 << epnum);
- UDP->UDP_RST_EP &= ~(1 << epnum);
+ UDP->UDP_RST_EP |= (1u << epnum);
+ UDP->UDP_RST_EP &= ~(1u << epnum);
}
//--------------------------------------------------------------------+
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 8a41c4790..6ed5fde8e 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -36,6 +36,8 @@
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#include "nrf.h"
@@ -461,7 +463,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
xfer->actual_len = 0;
// Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
- bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
+ bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir((uint8_t)NRF_USBD->BMREQUESTTYPE));
if (control_status) {
// The nRF doesn't interrupt on status transmit so we queue up a success response.
diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c
index d9a0e3fa8..2edb1bc7a 100644
--- a/src/portable/nuvoton/nuc120/dcd_nuc120.c
+++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -253,13 +253,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* mine the data for the information we need */
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- int const size = tu_edpt_packet_size(p_endpoint_desc);
+ uint16_t const size = tu_edpt_packet_size(p_endpoint_desc);
tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* allocate buffer from USB RAM */
ep->BUFSEG = bufseg_addr;
- bufseg_addr += size;
+ bufseg_addr += (uint32_t)size;
TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
/* construct USB Configuration Register value and then write it */
@@ -435,7 +435,7 @@ void dcd_int_handler(uint8_t rhport)
/* given ACK from host has happened, we can now set the address (if not already done) */
if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address;
- uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
+ uint16_t const available_bytes = (uint16_t)USBD->EP[PERIPH_EP0].MXPLD;
active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
@@ -453,7 +453,7 @@ void dcd_int_handler(uint8_t rhport)
{
USBD->INTSTS = mask;
- uint16_t const available_bytes = ep->MXPLD;
+ uint16_t const available_bytes = (uint16_t)ep->MXPLD;
uint8_t const ep_addr = decode_ep_addr(ep);
bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c
index 42fb58a0a..008c9df6b 100644
--- a/src/portable/nuvoton/nuc121/dcd_nuc121.c
+++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -42,6 +42,8 @@
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#include "NuMicro.h"
@@ -291,7 +293,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* mine the data for the information we need */
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- int const size = tu_edpt_packet_size(p_endpoint_desc);
+ uint16_t const size = tu_edpt_packet_size(p_endpoint_desc);
tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
@@ -478,7 +480,7 @@ void dcd_int_handler(uint8_t rhport)
{
if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */
{
- uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD;
+ uint16_t const available_bytes = (uint16_t)USBD->EP[PERIPH_EP0].MXPLD;
active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size);
@@ -496,7 +498,7 @@ void dcd_int_handler(uint8_t rhport)
{
USBD->INTSTS = mask;
- uint16_t const available_bytes = ep->MXPLD;
+ uint16_t const available_bytes = (uint16_t)ep->MXPLD;
uint8_t const ep_addr = decode_ep_addr(ep);
bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK);
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index ca17d6251..a0f3d4c3f 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -42,6 +42,8 @@
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
#include "NUC505Series.h"
@@ -338,13 +340,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
/* mine the data for the information we need */
int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- int const size = tu_edpt_packet_size(p_endpoint_desc);
+ uint16_t const size = tu_edpt_packet_size(p_endpoint_desc);
tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer;
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* allocate buffer from USB RAM */
ep->EPBUFSTART = bufseg_addr;
- bufseg_addr += size;
+ bufseg_addr += (uint32_t)size;
ep->EPBUFEND = bufseg_addr - 1;
TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE);
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
index 349229c8d..2840c6d5e 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
@@ -131,7 +131,7 @@ static uint8_t sie_read (uint8_t cmd_code)
//--------------------------------------------------------------------+
static inline uint8_t ep_addr2idx(uint8_t ep_addr)
{
- return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
+ return (uint8_t)(2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0));
}
static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size)
@@ -243,7 +243,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
static inline uint8_t byte2dword(uint8_t bytes)
{
- return (bytes + 3) / 4; // length in dwords
+ return (uint8_t)((bytes + 3) / 4); // length in dwords
}
static void control_ep_write(void const * buffer, uint8_t len)
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index 5f4a441dc..8adf0f840 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -243,7 +243,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t get_buf_offset(void const * buffer)
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t ep_addr2id(uint8_t ep_addr) {
- return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
+ return (uint8_t)(2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0));
}
TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_highspeed) {
@@ -539,8 +539,8 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status) {
uint16_t buf_nbytes;
if ( rhport_is_highspeed(rhport) ) {
- buf_offset = ep_cs->buffer_hs.offset;
- buf_nbytes = ep_cs->buffer_hs.nbytes;
+ buf_offset = (uint16_t)ep_cs->buffer_hs.offset;
+ buf_nbytes = (uint16_t)ep_cs->buffer_hs.nbytes;
#if TU_CHECK_MCU(OPT_MCU_LPC54)
// LPC54 Errata USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer
@@ -550,8 +550,8 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status) {
}
#endif
} else {
- buf_offset = ep_cs->buffer_fs.offset;
- buf_nbytes = ep_cs->buffer_fs.nbytes;
+ buf_offset = (uint16_t)ep_cs->buffer_fs.offset;
+ buf_nbytes = (uint16_t)ep_cs->buffer_fs.nbytes;
}
xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes;
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index e2a51a5ca..adbb53787 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -93,7 +93,9 @@ static unsigned find_pipe(unsigned xfer_type) {
const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
for (int i = idx_last; i >= idx_first; i--) {
- if (0 == _dcd.pipe[i].ep) return i;
+ if (0 == _dcd.pipe[i].ep) {
+ return (unsigned)i;
+ }
}
return 0;
@@ -117,10 +119,10 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) {
static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
rusb2_reg_t *rusb = RUSB2_REG(rhport);
- const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned epn = tu_edpt_number((uint8_t)ep_addr);
if (epn) {
- const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned dir = tu_edpt_dir((uint8_t)ep_addr);
const unsigned num = _dcd.ep[dir][epn];
return get_pipectr(rusb, num);
} else {
@@ -129,11 +131,11 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
}
static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) {
- return rusb->DCPMAXP_b.MXPS;
+ return (uint16_t)rusb->DCPMAXP_b.MXPS;
}
static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) {
- rusb->PIPESEL = num;
+ rusb->PIPESEL = (uint16_t)num;
return rusb->PIPEMAXP;
}
@@ -285,7 +287,7 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
const uint16_t mps = edpt_max_packet_size(rusb, num);
pipe_wait_for_ready(rusb, num);
- const uint16_t vld = rusb->D0FIFOCTR_b.DTLN;
+ const uint16_t vld = (uint16_t)rusb->D0FIFOCTR_b.DTLN;
const uint16_t len = tu_min16(tu_min16(rem, mps), vld);
void *buf = pipe->buf;
@@ -498,7 +500,7 @@ static void process_bus_reset(uint8_t rhport)
volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_CTR[0]));
volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_TR[0].E));
- for (int i = 1; i <= 5; ++i) {
+ for (uint16_t i = 1; i <= 5; ++i) {
rusb->PIPESEL = i;
rusb->PIPECFG = 0;
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk;
@@ -508,7 +510,7 @@ static void process_bus_reset(uint8_t rhport)
tre += 2;
}
- for (int i = 6; i <= 9; ++i) {
+ for (uint16_t i = 6; i <= 9; ++i) {
rusb->PIPESEL = i;
rusb->PIPECFG = 0;
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk;
@@ -542,7 +544,7 @@ static void process_bus_reset(uint8_t rhport)
static void process_set_address(uint8_t rhport)
{
rusb2_reg_t* rusb = RUSB2_REG(rhport);
- const uint16_t addr = rusb->USBADDR_b.USBADDR;
+ const uint16_t addr = (uint16_t)rusb->USBADDR_b.USBADDR;
if (!addr) {
return;
}
@@ -706,7 +708,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
(void)rhport;
rusb2_reg_t * rusb = RUSB2_REG(rhport);
- const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const uint8_t 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;
@@ -770,8 +772,10 @@ void dcd_edpt_close_all(uint8_t rhport)
dcd_int_disable(rhport);
while (--i) { /* Close all pipes except 0 */
const unsigned ep_addr = _dcd.pipe[i].ep;
- if (!ep_addr) continue;
- dcd_edpt_close(rhport, ep_addr);
+ if (!ep_addr) {
+ continue;
+ }
+ dcd_edpt_close(rhport, (uint8_t)ep_addr);
}
dcd_int_enable(rhport);
}
@@ -783,10 +787,10 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
const unsigned dir = tu_edpt_dir(ep_addr);
const unsigned num = _dcd.ep[dir][epn];
- rusb->BRDYENB &= ~TU_BIT(num);
+ rusb->BRDYENB &= (uint16_t)~TU_BIT(num);
volatile uint16_t *ctr = get_pipectr(rusb, num);
*ctr = 0;
- rusb->PIPESEL = num;
+ rusb->PIPESEL = (uint16_t)num;
rusb->PIPECFG = 0;
_dcd.pipe[num].ep = 0;
_dcd.ep[dir][epn] = 0;
@@ -860,7 +864,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
*ctr = RUSB2_PIPE_CTR_PID_BUF;
} else {
const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)];
- rusb->PIPESEL = num;
+ rusb->PIPESEL = (uint16_t)num;
if (rusb->PIPECFG_b.TYPE != 1) {
*ctr = RUSB2_PIPE_CTR_PID_BUF;
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 8b4719b21..41da3ddd0 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -259,7 +259,7 @@ static void handle_ctr_tx(uint32_t ep_id) {
}
if (xfer->total_len != xfer->queued_len) {
- dcd_transmit_packet(xfer, ep_id);
+ dcd_transmit_packet(xfer, (uint16_t)ep_id);
} else {
dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true);
}
@@ -267,7 +267,7 @@ static void handle_ctr_tx(uint32_t ep_id) {
static void handle_ctr_setup(uint32_t ep_id) {
uint16_t rx_count = btable_get_count(ep_id, BTABLE_BUF_RX);
- uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
+ uint16_t rx_addr = (uint16_t)btable_get_addr(ep_id, BTABLE_BUF_RX);
uint8_t setup_packet[8] TU_ATTR_ALIGNED(4);
tu_hwfifo_read(PMA_BUF_AT(rx_addr), setup_packet, rx_count, NULL);
@@ -393,26 +393,8 @@ void dcd_int_handler(uint8_t rhport) {
const uint32_t ep_reg = ep_read(ep_id);
if (ep_reg & U_EP_CTR_RX) {
- #ifdef CFG_TUSB_FSDEV_32BIT
- /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
- * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
- * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers
- * Description:
- * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM
- * accesses have completed. If the software responds quickly to the interrupt, the full buffer contents may not be
- * correct. Workaround:
- * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
- * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
- * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code
- * also takes time, so we'll wait 60 cycles (count = 20).
- * - Since Low Speed mode is not supported/popular, we will ignore it for now.
- *
- * Note: this errata may also apply to G0, U5, H5 etc.
- */
- volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver
- while (cycle_count > 0U) {
- cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
- }
+ #if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT)
+ fsdev_btable_workaround_delay(false);
#endif
if (ep_reg & U_EP_SETUP) {
@@ -531,8 +513,8 @@ void edpt0_open(uint8_t rhport) {
xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
xfer_status[0][1].ep_idx = 0;
- uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
- uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+ uint16_t pma_addr0 = (uint16_t)dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+ uint16_t pma_addr1 = (uint16_t)dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
btable_set_addr(0, BTABLE_BUF_RX, pma_addr0);
btable_set_addr(0, BTABLE_BUF_TX, pma_addr1);
@@ -574,7 +556,7 @@ bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
}
/* Create a packet memory buffer area. */
- uint16_t pma_addr = dcd_pma_alloc(packet_size, false);
+ uint16_t pma_addr = (uint16_t)dcd_pma_alloc(packet_size, false);
btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
@@ -624,17 +606,17 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
#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;
+ uint16_t pma_addr2 = (uint16_t)(pma_addr >> 16);
#else
uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false);
- uint16_t pma_addr2 = pma_addr;
+ uint16_t pma_addr2 = (uint16_t)pma_addr;
#endif
#if FSDEV_USE_SBUF_ISO == 0
- btable_set_addr(ep_idx, 0, pma_addr);
+ btable_set_addr(ep_idx, 0, (uint16_t)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);
+ btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, (uint16_t)pma_addr);
(void)pma_addr2;
#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c
index 003bcd069..7c4572a1e 100644
--- a/src/portable/st/stm32_fsdev/fsdev_common.c
+++ b/src/portable/st/stm32_fsdev/fsdev_common.c
@@ -81,11 +81,11 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_bloc
if (size > 62) {
block_in_bytes = 32;
*blsize = 1;
- *num_block = tu_div_ceil(size, 32);
+ *num_block = (uint8_t)tu_div_ceil(size, 32);
} else {
block_in_bytes = 2;
*blsize = 0;
- *num_block = tu_div_ceil(size, 2);
+ *num_block = (uint8_t)tu_div_ceil(size, 2);
}
return (*num_block) * block_in_bytes;
diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h
index 140ff1d61..af84b8b97 100644
--- a/src/portable/st/stm32_fsdev/fsdev_common.h
+++ b/src/portable/st/stm32_fsdev/fsdev_common.h
@@ -345,7 +345,7 @@ TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb
uint32_t reg = FSDEV_REG->ep[ep_id].reg;
reg |= U_EP_CTR_TX | U_EP_CTR_RX;
reg &= U_EPREG_MASK;
- reg &= ~(1 << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+ reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0u : 8u)));
ep_write(ep_id, reg, false);
}
@@ -378,7 +378,7 @@ TU_ATTR_ALWAYS_INLINE static inline void ch_write_clear_ctr(uint32_t ch_id, tusb
uint32_t reg = FSDEV_REG->ep[ch_id].reg;
reg |= U_EP_CTR_TX | U_EP_CTR_RX;
reg &= U_EPREG_MASK;
- reg &= ~(1 << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
+ reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8u : 0u)));
ep_write(ch_id, reg, false);
}
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
index a63592c5d..070aa00ec 100644
--- a/src/portable/st/stm32_fsdev/fsdev_stm32.h
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -252,6 +252,49 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) {
}
//--------------------------------------------------------------------+
+// STM32 FSDEV PMA Buffer Description Table errata workaround
+//--------------------------------------------------------------------+
+
+#ifdef CFG_TUSB_FSDEV_32BIT
+/* Errata: Buffer description table update completes after CTR interrupt triggers
+ * https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
+ * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
+ *
+ * CTR may trigger before final PMA SRAM accesses complete on OUT transfers.
+ * Insert delay before reading PMA count/data.
+ * Max CPU frequency in MHz, used to derive conservative FSDEV PMA delay defaults.
+ */
+#if CFG_TUSB_MCU == OPT_MCU_STM32H5
+ #define FSDEV_STM32_CPU_MHZ 250U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ #define FSDEV_STM32_CPU_MHZ 160U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
+ #define FSDEV_STM32_CPU_MHZ 96U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
+ #define FSDEV_STM32_CPU_MHZ 56U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #define FSDEV_STM32_CPU_MHZ 64U
+#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #define FSDEV_STM32_CPU_MHZ 48U
+#endif
+
+#ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT
+ #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ / 4U)
+#endif
+
+#ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT
+ #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ * 2U)
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) {
+ volatile uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT;
+ while (cycle_count > 0U) {
+ cycle_count--;
+ }
+}
+#endif
+
+//--------------------------------------------------------------------+
// Connect / Disconnect
//--------------------------------------------------------------------+
diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c
index 18685dbdc..f9201651a 100644
--- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c
@@ -58,20 +58,6 @@
TU_VERIFY_STATIC(CFG_TUH_FSDEV_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
-#if CFG_TUSB_MCU == OPT_MCU_STM32H5
- #define CPU_FREQUENCY_MHZ 250U
-#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
- #define CPU_FREQUENCY_MHZ 160U
-#elif CFG_TUSB_MCU == OPT_MCU_STM32U3
- #define CPU_FREQUENCY_MHZ 96U
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
- #define CPU_FREQUENCY_MHZ 64U
-#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
- #define CPU_FREQUENCY_MHZ 48U
-#else
- #error "CPU_FREQUENCY_MHZ not defined for this STM32 MCU"
-#endif
-
enum {
HCD_XFER_ERROR_MAX = 3,
HCD_XFER_NAK_MAX = 15,
@@ -165,35 +151,9 @@ static inline void channel_write_status(uint8_t ch_id, uint32_t ch_reg, tusb_dir
}
static inline uint16_t channel_get_rx_count(uint8_t ch_id) {
- /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
- * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
- * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers
- * Description:
- * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses
- * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct.
- * Workaround:
- * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
- * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
- *
- * Note: this errata may also apply to G0, U5, H5 etc.
- *
- * We choose the delay count based on max CPU frequency (in MHz) to ensure the delay is at least the required time.
- */
-
uint32_t ch_reg = ch_read(ch_id);
- if (FSDEV_REG->ISTR & U_ISTR_LS_DCONN || ch_reg & U_EP_LSEP) {
- // Low speed mode: 6.4 us delay -> about 2 cycles per MHz
- volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ * 2U;
- while (cycle_count > 0U) {
- cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
- }
- } else {
- // Full speed mode: 800 ns delay -> about 0.25 cycles per MHz
- volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U;
- while (cycle_count > 0U) {
- cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
- }
- }
+ const bool is_low_speed = (FSDEV_REG->ISTR & U_ISTR_LS_DCONN) || (ch_reg & U_EP_LSEP);
+ fsdev_btable_workaround_delay(is_low_speed);
return btable_get_count(ch_id, BTABLE_BUF_RX);
}
@@ -237,11 +197,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt
if (FSDEV_REG->ISTR & U_ISTR_DCON_STAT) {
- // Wait DP/DM stabilize time
- volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U;
- while (cycle_count > 0U) {
- cycle_count--;
- }
+ tusb_time_delay_ms_api(2);
port_status_handler(rhport, false);
}
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 9a9c734a0..30e24a9ad 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -191,7 +191,7 @@ static void dma_setup_prepare(uint8_t rhport) {
*/
TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
- return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count;
+ return (uint16_t)(13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count);
}
static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size, bool is_bulk) {
@@ -203,7 +203,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size, b
TU_ASSERT(epnum < ep_count);
- uint16_t fifo_size = tu_div_ceil(packet_size, 4);
+ uint16_t fifo_size = (uint16_t)tu_div_ceil(packet_size, 4);
if (dir == TUSB_DIR_OUT) {
// Calculate required size of RX FIFO
const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count);
@@ -371,7 +371,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
num_packets = 1;
} else {
total_bytes = xfer->total_len;
- num_packets = tu_div_ceil(total_bytes, xfer->max_size);
+ num_packets = (uint16_t)tu_div_ceil(total_bytes, xfer->max_size);
if (num_packets == 0) {
num_packets = 1; // zero length packet still count as 1
}
@@ -541,8 +541,9 @@ void dcd_remote_wakeup(uint8_t rhport) {
void dcd_connect(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-#ifdef TUP_USBIP_DWC2_ESP32
- // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY
+#if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31)
+ // S31 is excluded at compile time (no USB_WRAP peripheral).
+ // On P4, the HS PHY (port 1) must not touch USB_WRAP which belongs to the FS PHY.
if (rhport == 0) {
usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
conf.pad_pull_override = 0;
@@ -560,8 +561,9 @@ void dcd_connect(uint8_t rhport) {
void dcd_disconnect(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-#ifdef TUP_USBIP_DWC2_ESP32
- // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY
+#if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31)
+ // S31 is excluded at compile time (no USB_WRAP peripheral).
+ // On P4, the HS PHY (port 1) must not touch USB_WRAP which belongs to the FS PHY.
if (rhport == 0) {
usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
conf.pad_pull_override = 1;
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 6a10dc7f8..436f8dc30 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -37,7 +37,11 @@
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
+
+// ESP32-S31 does not have USB_WRAP peripheral (HS-only with UTMI PHY)
+#if !TU_CHECK_MCU(OPT_MCU_ESP32S31)
#include "soc/usb_wrap_struct.h"
+#endif
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
#define DWC2_FS_REG_BASE 0x60080000UL
@@ -75,6 +79,14 @@ 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, .otg_dfifo_depth = 256 },
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 }
};
+
+#elif TU_CHECK_MCU(OPT_MCU_ESP32S31)
+#define DWC2_HS_REG_BASE 0x20300000UL
+#define DWC2_EP_MAX 16
+
+static const dwc2_controller_t _dwc2_controller[] = {
+ { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTGHS_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 }
+};
#endif
//--------------------------------------------------------------------+
diff --git a/src/tinyusb.mk b/src/tinyusb.mk
index e7d1c0b5b..169098016 100644
--- a/src/tinyusb.mk
+++ b/src/tinyusb.mk
@@ -26,4 +26,3 @@ TINYUSB_SRC_C += \
src/class/midi/midi_host.c \
src/class/msc/msc_host.c \
src/class/vendor/vendor_host.c \
- src/typec/usbc.c \
diff --git a/src/tusb_option.h b/src/tusb_option.h
index dd7af76f6..154f8e2a4 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -134,6 +134,7 @@
#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 OPT_MCU_ESP32S31 911 ///< Espressif ESP32-S31
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index d50a60894..e98bd5da7 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -58,7 +58,9 @@ STATUS_SKIPPED = "\033[33mSkipped\033[0m"
verbose = False
test_only = []
+board_test = {}
build_dir = 'cmake-build'
+skip_flash = False
WCH_RISCV_CONTENT = """
adapter driver wlinke
@@ -737,56 +739,81 @@ def test_device_cdc_msc(board):
data = read_disk_file(uid, 0, 'README.TXT')
assert data == MSC_README_TXT, f'MSC wrong data in README.TXT\n expected: {MSC_README_TXT.decode()}\n received: {data.decode()}'
- # MSC dd throughput test: read all sectors then write back same data
+
+def test_device_cdc_msc_freertos(board):
+ test_device_cdc_msc(board)
+
+
+def test_device_cdc_msc_throughput(board):
+ uid = board['uid']
+
+ def parse_speed(dd_output):
+ for line in dd_output.splitlines():
+ m = re.search(r'([\d.]+)\s+([kMG]?B)/s', line)
+ if m:
+ return f'{float(m.group(1)):.1f} {m.group(2)}ps'
+ return '?'
+
+ # Wait for MSC disk enumeration
dev = get_disk_dev(uid, 'TinyUSB', 0)
timeout = ENUM_TIMEOUT
while timeout > 0:
if os.path.exists(dev):
break
- time.sleep(1)
- timeout -= 1
- assert timeout > 0, f'Disk {dev} not found for dd test'
+ time.sleep(0.1); timeout -= 0.1
+ assert timeout > 0, f'Disk {dev} not found'
- block_count = 16
- block_size = 512
- tmp_file = f'/tmp/msc_dd_{uid}.bin'
+ # Wait for CDC tty enumeration
+ tty = get_serial_dev(uid, 'TinyUSB', 'Throughput', 0)
+ timeout = ENUM_TIMEOUT
+ while timeout > 0:
+ if os.path.exists(tty):
+ break
+ time.sleep(0.1); timeout -= 0.1
+ assert timeout > 0, f'CDC tty {tty} not found'
+
+ # Detect speed (12 Mbps FS / 480 Mbps HS) for payload scaling
+ is_fs = False
+ for f in glob.glob('/sys/bus/usb/devices/*/serial'):
+ try:
+ if open(f).read().strip() == uid:
+ is_fs = (open(os.path.join(os.path.dirname(f), 'speed')).read().strip() == '12')
+ break
+ except (OSError, ValueError):
+ pass
- # dd reports speed based on payload only. Each block also transfers 31-byte CBW + 13-byte CSW on USB.
- scsi_ratio = (block_size + 31 + 13) / block_size
+ # Put tty in raw mode so dd sees pure binary throughput.
+ rs = run_cmd(f'timeout 30 stty -F {tty} raw -echo')
+ assert rs.returncode == 0, f'stty failed: {rs.stdout.decode()}'
- def parse_dd_speed(dd_output):
- """Parse dd output, return USB-adjusted speed string"""
- for line in dd_output.splitlines():
- m = re.search(r'([\d.]+)\s+([kMG]?B/s)', line)
- if m:
- speed_val = float(m.group(1)) * scsi_ratio
- return f'{speed_val:.1f} {m.group(2)}'
- return ''
+ # Payload aim: ~5 s per direction at FS (~830 kB/s), much less at HS.
+ msc_count = 2 if is_fs else 16 # bs=1M
+ cdc_count = 16 if is_fs else 128 # bs=64K
- # Read: dd from device to file
- ret = run_cmd(f'dd if={dev} of={tmp_file} bs={block_size} count={block_count} iflag=direct 2>&1')
- assert ret.returncode == 0, f'dd read failed: {ret.stdout.decode()}'
- read_speed = parse_dd_speed(ret.stdout.decode())
+ tmp_file = f'/tmp/cdc_msc_tp_{uid}.bin'
- # Write back the same data to avoid corrupting the disk (skip if read-only)
- ret = run_cmd(f'dd if={tmp_file} of={dev} bs={block_size} count={block_count} oflag=direct 2>&1')
- if ret.returncode != 0 and 'Read-only' in ret.stdout.decode():
- write_speed = 'skip (read-only)'
- else:
- assert ret.returncode == 0, f'dd write failed: {ret.stdout.decode()}'
- write_speed = parse_dd_speed(ret.stdout.decode())
+ rw = run_cmd(f'timeout 30 dd if=/dev/zero of={tty} bs=64K count={cdc_count} 2>&1')
+ assert rw.returncode == 0, f'CDC dd write failed: {rw.stdout.decode()}'
+ cdc_w = parse_speed(rw.stdout.decode())
+
+ rr = run_cmd(f'timeout 30 dd if={tty} of=/dev/null bs=64K count={cdc_count} iflag=fullblock 2>&1')
+ assert rr.returncode == 0, f'CDC dd read failed: {rr.stdout.decode()}'
+ cdc_r = parse_speed(rr.stdout.decode())
+
+ rmr = run_cmd(f'dd if={dev} of={tmp_file} bs=1M count={msc_count} iflag=direct 2>&1')
+ assert rmr.returncode == 0, f'MSC dd read failed: {rmr.stdout.decode()}'
+ msc_r = parse_speed(rmr.stdout.decode())
+
+ rmw = run_cmd(f'dd if={tmp_file} of={dev} bs=1M count={msc_count} oflag=direct 2>&1')
+ assert rmw.returncode == 0, f'MSC dd write failed: {rmw.stdout.decode()}'
+ msc_w = parse_speed(rmw.stdout.decode())
try:
os.remove(tmp_file)
except OSError:
pass
- if read_speed and write_speed:
- print(f' dd read: {read_speed}, write: {write_speed}', end='')
-
-
-def test_device_cdc_msc_freertos(board):
- test_device_cdc_msc(board)
+ print(f' CDC read {cdc_r} write {cdc_w}, MSC read {msc_r} write {msc_w} ', end='')
def test_device_dfu(board):
@@ -1028,6 +1055,65 @@ def test_device_mtp(board):
mtp.disconnect()
+def test_device_net_lwip_webserver(board):
+ # MAC hard-coded in examples/device/net_lwip_webserver/src/main.c; Linux names the
+ # USB network interface enx<MAC_lowercase_no_colons>. Device IP is 192.168.7.1 and
+ # the example runs an iperf2 TCP server on port 5001 (INCLUDE_IPERF).
+ import socket
+ mac_no_colons = '0202846a9600'
+ iface = 'enx' + mac_no_colons
+ device_ip = '192.168.7.1'
+ iperf_port = 5001
+
+ # Wait for the host to get an IPv4 address in the device's subnet (DHCP served by the device).
+ # USB enum + DHCP serve can take longer on the CI HIL hardware than on local — give it 30s.
+ iface_timeout = 30
+ deadline = time.time() + iface_timeout
+ host_ip = None
+ while time.time() < deadline:
+ ret = subprocess.run(['ip', '-o', '-4', 'addr', 'show', iface],
+ capture_output=True, text=True, timeout=2)
+ m = re.search(r'inet (192\.168\.7\.\d+)/', ret.stdout) if ret.returncode == 0 else None
+ if m:
+ host_ip = m.group(1)
+ break
+ time.sleep(0.5)
+ assert host_ip, f'USB net iface {iface} did not come up with 192.168.7.x within {iface_timeout}s'
+
+ # Poll the iperf TCP port until the device is accepting. The net stack comes up a bit
+ # after DHCP completes; iperf server binding isn't instantaneous after reflash.
+ deadline = time.time() + ENUM_TIMEOUT
+ last_err = None
+ while time.time() < deadline:
+ try:
+ with socket.create_connection((device_ip, iperf_port), timeout=1):
+ last_err = None
+ break
+ except OSError as e:
+ last_err = e
+ time.sleep(0.3)
+ assert last_err is None, f'iperf TCP {device_ip}:{iperf_port} not accepting within {ENUM_TIMEOUT}s: {last_err}'
+
+ # Throughput: 5-second iperf2 TCP test, CSV output for stable parsing.
+ # iperf2 CSV final summary line: timestamp,src_ip,src_port,dst_ip,dst_port,id,interval,bytes,bps
+ ret = subprocess.run(['iperf', '-c', device_ip, '-t', '5', '-y', 'C'],
+ capture_output=True, text=True, timeout=30)
+ stderr = ret.stderr.strip()
+ stdout = ret.stdout.strip()
+ assert ret.returncode == 0, f'iperf rc={ret.returncode}: stderr={stderr!r} stdout={stdout!r}'
+ lines = [l for l in stdout.splitlines() if l]
+ assert lines, f'iperf produced no output (rc={ret.returncode}, stderr={stderr!r})'
+ try:
+ bps = int(lines[-1].split(',')[-1])
+ except (ValueError, IndexError) as e:
+ raise AssertionError(f'could not parse iperf output: {lines[-1]!r} ({e})')
+ mbps = bps / 1e6
+ print(f' iperf {mbps:5.1f} Mbps', end='')
+
+ # Reject implausibly low throughput - a working USB-net link should clear this easily.
+ assert mbps >= 1.0, f'iperf throughput too low: {mbps:.2f} Mbps'
+
+
def test_device_msc_dual_lun(board):
uid = board['uid']
@@ -1143,6 +1229,7 @@ device_tests = [
'device/cdc_dual_ports',
'device/dfu',
'device/cdc_msc',
+ 'device/cdc_msc_throughput',
'device/dfu_runtime',
'device/cdc_msc_freertos',
'device/hid_boot_interface',
@@ -1150,7 +1237,8 @@ device_tests = [
'device/hid_generic_inout',
'device/printer_to_cdc',
'device/midi_test',
- 'device/mtp'
+ 'device/mtp',
+ # 'device/net_lwip_webserver', # disabled for PR #3605: USB net iface enum is flaky on the CI HIL host
]
dual_tests = [
@@ -1190,11 +1278,15 @@ def test_example(board, f1, example):
if verbose:
print(f'Flashing {fw_name}.elf')
- # flash firmware. It may fail randomly, retry a few times
+ # flash firmware (unless --skip-flash), then run the test. Both may fail randomly,
+ # retry a few times.
start_s = time.time()
+ flash_ok = True
for i in range(max_retry):
- ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name)
- if ret.returncode == 0:
+ if not skip_flash:
+ ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name)
+ flash_ok = (ret.returncode == 0)
+ if flash_ok:
try:
tret = globals()[f'test_{example.replace("/", "_")}'](board)
if tret == 'skipped':
@@ -1213,7 +1305,7 @@ def test_example(board, f1, example):
print(f'\n Flash failed, retry {i+2}/{max_retry}', end='')
time.sleep(0.5)
- if ret.returncode != 0:
+ if not flash_ok:
err_count += 1
print(f' Flash {STATUS_FAILED}', end='')
@@ -1222,6 +1314,32 @@ def test_example(board, f1, example):
return err_count
+def build_board(board):
+ """Build firmware for this board via tools/build.py.
+ Honors board config's build.flags_on variants and build.args defines.
+ Output goes to cmake-build/cmake-build-BOARD[-f1_...]/ (tools/build.py layout)."""
+ name = board['name']
+ bcfg = board.get('build', {})
+ flags_on_list = bcfg.get('flags_on', [''])
+ extra_defs = bcfg.get('args', [])
+
+ failed = 0
+ for f1 in flags_on_list:
+ cmd = [sys.executable, f'{TINYUSB_ROOT}/tools/build.py', '-b', name]
+ for d in extra_defs:
+ cmd += ['-D', d]
+ if f1:
+ for flag in f1.split():
+ cmd += ['-f1', flag]
+ if verbose:
+ cmd.append('-v')
+ print(f' + {" ".join(cmd)}')
+ r = subprocess.run(cmd, cwd=TINYUSB_ROOT)
+ if r.returncode != 0:
+ failed += 1
+ return name, failed
+
+
def test_board(board):
name = board['name']
flasher = board['flasher']
@@ -1229,7 +1347,9 @@ def test_board(board):
# default to all tests
test_list = []
- if len(test_only) > 0:
+ if name in board_test:
+ test_list = board_test[name]
+ elif len(test_only) > 0:
test_list = test_only
else:
if 'tests' in board:
@@ -1249,18 +1369,23 @@ def test_board(board):
print(f'{name:25} {skip:30} ... Skip')
err_count = 0
+ failed_tests = []
flags_on_list = [""]
if 'build' in board and 'flags_on' in board['build']:
flags_on_list = board['build']['flags_on']
for f1 in flags_on_list:
for test in test_list:
- err_count += test_example(board, f1, test)
+ ec = test_example(board, f1, test)
+ err_count += ec
+ if ec > 0:
+ failed_tests.append(test)
- # flash board_test last to disable board's usb
- test_example(board, flags_on_list[0], 'device/board_test')
+ # flash board_test last to disable board's usb (skipped when --skip-flash is set)
+ if not skip_flash:
+ test_example(board, flags_on_list[0], 'device/board_test')
- return name, err_count
+ return name, err_count, sorted(set(failed_tests))
def main():
@@ -1269,28 +1394,40 @@ def main():
"""
global verbose
global test_only
+ global board_test
global build_dir
global max_retry
+ global skip_flash
duration = time.time()
parser = argparse.ArgumentParser()
parser.add_argument('config_file', help='Configuration JSON file')
parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified')
- parser.add_argument('-s', '--skip', action='append', default=[], help='Skip boards from test')
+ parser.add_argument('-s', '--skip-board', action='append', default=[], help='Skip boards from test')
+ parser.add_argument('-sf', '--skip-flash', action='store_true', help='Run tests without flashing firmware (use whatever is already on the board)')
parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified')
- parser.add_argument('-B', '--build', default='cmake-build', help='Build folder name (default: cmake-build)')
+ parser.add_argument('-bt', '--board-test', action='append', default=[],
+ help='Per-board test list as BOARD:test1,test2 (overrides -t for that board); repeat for multiple boards')
+ parser.add_argument('-B', '--build-dir', default='cmake-build', help='Build folder name (default: cmake-build)')
+ parser.add_argument('--build', action='store_true', help='Build firmware for selected boards with cmake before running tests')
parser.add_argument('-r', '--retry', type=int, default=3, help='Retry count for failed tests (default: 3)')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
config_file = args.config_file
boards = args.board
- skip_boards = args.skip
+ skip_boards = args.skip_board
verbose = args.verbose
test_only = args.test_only
- build_dir = args.build
+ for entry in args.board_test:
+ bname, _, tnames = entry.partition(':')
+ if not bname or not tnames:
+ parser.error(f'invalid --board-test value: {entry!r} (expected BOARD:test1,test2)')
+ board_test[bname] = [t for t in tnames.split(',') if t]
+ build_dir = args.build_dir
max_retry = args.retry
+ skip_flash = args.skip_flash
# if config file is not found, try to find it in the same directory as this script
if not os.path.exists(config_file):
@@ -1303,16 +1440,33 @@ def main():
else:
config_boards = [e for e in config['boards'] if e['name'] in boards]
- err_count = 0
+ build_err = 0
+ if args.build:
+ if build_dir != 'cmake-build':
+ print(f'warning: --build writes into cmake-build/, but -B is {build_dir!r}; '
+ f'tests will not find the freshly built firmware')
+ print('-' * 30)
+ print(f'Build phase: {len(config_boards)} board(s)')
+ print('-' * 30)
+ for board in config_boards:
+ _, nfail = build_board(board)
+ build_err += nfail
+ print('-' * 30)
+ print(f'Build phase done: {build_err} failed')
+ print('-' * 30)
+
with Pool(processes=os.cpu_count()) as pool:
mret = pool.map(test_board, config_boards)
- err_count = sum(e[1] for e in mret)
- # generate skip list for next re-run if failed
+ err_count = build_err + sum(e[1] for e in mret)
+ # generate skip list for next re-run if failed: skip boards that fully passed,
+ # and emit -bt BOARD:t1,t2 so each failed board only re-runs its own failed tests.
skip_fname = f'{config_file}.skip'
if err_count > 0:
- skip_boards += [name for name, err in mret if err == 0]
+ skip_boards += [name for name, err, _ in mret if err == 0]
+ parts = [f'--skip-board {i}' for i in skip_boards]
+ parts += [f'-bt {name}:{",".join(fts)}' for name, err, fts in mret if err > 0 and fts]
with open(skip_fname, 'w') as f:
- f.write(' '.join(f'-s {i}' for i in skip_boards))
+ f.write(' '.join(parts))
elif os.path.exists(skip_fname):
os.remove(skip_fname)
diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json
index 92b7b21b0..a3f7ff8bf 100644
--- a/test/hil/tinyusb.json
+++ b/test/hil/tinyusb.json
@@ -43,7 +43,7 @@
},
"flasher": {
"name": "jlink",
- "uid": "000682804350",
+ "uid": "681295394",
"args": "-device nrf52840_xxaa"
}
},
@@ -184,11 +184,6 @@
"device": false, "host": true, "dual": false,
"dev_attached": [
{
- "vid_pid": "1a86_55d4",
- "serial": "52D2002694",
- "is_cdc": true
- },
- {
"vid_pid": "0951_1603",
"serial": "820000000000000045B46338",
"is_msc": true,
@@ -226,6 +221,9 @@
{
"name": "stm32f072disco",
"uid": "3A001A001357364230353532",
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
"flasher": {
"name": "jlink",
"uid": "779541626",