From 31bd4fc169eb002ded3f23e8a6a0b95f3f591efb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 05:28:31 +0000 Subject: Add is_isr parameter to dcd_edpt_xfer and dcd_edpt_xfer_fifo Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/raspberrypi/pio_usb/dcd_pio_usb.c | 8 +++++--- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c index 60afbd435..3653544d9 100644 --- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c +++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c @@ -1,3 +1,4 @@ + /* * The MIT License (MIT) * @@ -76,7 +77,7 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr) { // must be called before queuing status pio_usb_device_set_address(dev_addr); - dcd_edpt_xfer(rhport, 0x80, NULL, 0); + dcd_edpt_xfer(rhport, 0x80, NULL, 0, false); } // Wake up host @@ -114,15 +115,16 @@ 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 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; endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_addr); return pio_usb_ll_transfer_start(ep, buffer, total_bytes); } // Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - 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 dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes, bool is_isr) //{ // (void) rhport; // (void) ep_addr; diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index a89c2f42b..0a7c250c2 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -1,3 +1,4 @@ + /* * The MIT License (MIT) * @@ -526,7 +527,8 @@ void dcd_edpt_close_all(uint8_t rhport) { reset_non_control_endpoints(); } -bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { +bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) { + (void) is_isr; assert(rhport == 0); hw_endpoint_xfer(ep_addr, buffer, total_bytes); return true; -- cgit v1.3.1 From 925ba123dac77b1675abeb2455762b52fffdc29b Mon Sep 17 00:00:00 2001 From: Zixun LI Date: Wed, 22 Oct 2025 21:08:09 +0200 Subject: Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/portable/bridgetek/ft9xx/dcd_ft9xx.c | 1 - src/portable/chipidea/ci_fs/dcd_ci_fs.c | 1 - src/portable/chipidea/ci_hs/dcd_ci_hs.c | 1 - src/portable/dialog/da146xx/dcd_da146xx.c | 1 - src/portable/mentor/musb/dcd_musb.c | 1 - src/portable/microchip/pic/dcd_pic.c | 1 - src/portable/microchip/pic32mz/dcd_pic32mz.c | 1 - src/portable/microchip/samd/dcd_samd.c | 1 - src/portable/microchip/samg/dcd_samg.c | 1 - src/portable/microchip/samx7x/dcd_samx7x.c | 1 - src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c | 1 - src/portable/nordic/nrf5x/dcd_nrf5x.c | 1 - src/portable/nuvoton/nuc120/dcd_nuc120.c | 1 - src/portable/nuvoton/nuc121/dcd_nuc121.c | 1 - src/portable/nuvoton/nuc505/dcd_nuc505.c | 1 - src/portable/nxp/khci/dcd_khci.c | 1 - src/portable/nxp/lpc17_40/dcd_lpc17_40.c | 1 - src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c | 1 - src/portable/raspberrypi/pio_usb/dcd_pio_usb.c | 1 - src/portable/raspberrypi/rp2040/dcd_rp2040.c | 1 - src/portable/renesas/rusb2/dcd_rusb2.c | 1 - src/portable/sony/cxd56/dcd_cxd56.c | 1 - src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 1 - src/portable/sunxi/dcd_sunxi_musb.c | 1 - src/portable/synopsys/dwc2/dcd_dwc2.c | 1 - src/portable/template/dcd_template.c | 1 - src/portable/ti/msp430x5xx/dcd_msp430x5xx.c | 1 - src/portable/valentyusb/eptri/dcd_eptri.c | 1 - src/portable/wch/dcd_ch32_usbfs.c | 1 - src/portable/wch/dcd_ch32_usbhs.c | 1 - 30 files changed, 30 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c index f6def44ab..76a54e62e 100644 --- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c +++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c index 283baf645..558d64b57 100644 --- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c +++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c index a11d2bbd1..808c293a5 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 8666992aa..5beb62b4d 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index acb35fcee..1e4ec0015 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c index 7d92056e8..3114295de 100644 --- a/src/portable/microchip/pic/dcd_pic.c +++ b/src/portable/microchip/pic/dcd_pic.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c index ce53893ec..a903c3ae2 100644 --- a/src/portable/microchip/pic32mz/dcd_pic32mz.c +++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index 1ff314d39..8293273e5 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 3ba538639..c6c7f15d1 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c index 494c83fde..57c0fcb4d 100644 --- a/src/portable/microchip/samx7x/dcd_samx7x.c +++ b/src/portable/microchip/samx7x/dcd_samx7x.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c index 6c0302031..929cb40c4 100644 --- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c +++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index a2d634fe6..89dff4995 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c index bb1d11355..d9a0e3fa8 100644 --- a/src/portable/nuvoton/nuc120/dcd_nuc120.c +++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 067d455c6..a4dfe24fa 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index f7593cf25..25e3f2203 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c index 3b0de47f4..8941ca766 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c index 81220e850..364cf8de4 100644 --- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c index 2257d0368..143b0277c 100644 --- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c +++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c index 3653544d9..5e3dd7faf 100644 --- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c +++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 0a7c250c2..15852f29f 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index 9b875f0d1..6ac1c5ee4 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/sony/cxd56/dcd_cxd56.c b/src/portable/sony/cxd56/dcd_cxd56.c index 664952b83..be694edfa 100644 --- a/src/portable/sony/cxd56/dcd_cxd56.c +++ b/src/portable/sony/cxd56/dcd_cxd56.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 640481442..381aa0b40 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index 52d69ad01..46d05e3e6 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index ab30a06e8..ad78708b6 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/template/dcd_template.c b/src/portable/template/dcd_template.c index ff7995c11..90c672d19 100644 --- a/src/portable/template/dcd_template.c +++ b/src/portable/template/dcd_template.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c index def55b59b..10c7d78c5 100644 --- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c +++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index a389aa761..760f3c90a 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c index c5b610b82..eb2ebd868 100644 --- a/src/portable/wch/dcd_ch32_usbfs.c +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index cf2deab78..a18eb3e81 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -1,4 +1,3 @@ - /* * The MIT License (MIT) * -- cgit v1.3.1 From d55e074a36de0331006fca1a19a241e98f9bd7e1 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 27 Oct 2025 17:11:42 +0700 Subject: improve warnings with rp2040 family --- hw/bsp/family_support.cmake | 55 ++------------------- hw/bsp/rp2040/family.c | 2 +- hw/bsp/rp2040/family.cmake | 66 +++++++++++++++----------- src/portable/raspberrypi/pio_usb/hcd_pio_usb.c | 11 +++++ 4 files changed, 54 insertions(+), 80 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake index 912e0f4d7..16c0d48d7 100644 --- a/hw/bsp/family_support.cmake +++ b/hw/bsp/family_support.cmake @@ -63,8 +63,9 @@ set(WARN_FLAGS_GNU -Wunused -Wunused-function -Wreturn-type - #-Wredundant-decls - #-Wmissing-prototypes + -Wredundant-decls + -Wmissing-prototypes +# -Wconversion ) set(WARN_FLAGS_Clang ${WARN_FLAGS_GNU}) @@ -391,56 +392,6 @@ function(family_example_missing_dependency TARGET DEPENDENCY) message(WARNING "${DEPENDENCY} submodule needed by ${TARGET} not found, please run 'python tools/get_deps.py ${DEPENDENCY}' to fetch it") endfunction() -#---------------------------------- -# RPI specific: refactor later -#---------------------------------- -function(family_add_default_example_warnings TARGET) - target_compile_options(${TARGET} PUBLIC - -Wall - -Wextra - -Werror - -Wfatal-errors - -Wdouble-promotion - -Wfloat-equal - # FIXME commented out because of https://github.com/raspberrypi/pico-sdk/issues/1468 - #-Wshadow - -Wwrite-strings - -Wsign-compare - -Wmissing-format-attribute - -Wunreachable-code - -Wcast-align - -Wcast-qual - -Wnull-dereference - -Wuninitialized - -Wunused - -Wredundant-decls - #-Wstrict-prototypes - #-Werror-implicit-function-declaration - #-Wundef - ) - - if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED) - target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments") - endif() - - # GCC 10 - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) - target_compile_options(${TARGET} PUBLIC -Wconversion) - endif() - - # GCC 8 - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) - target_compile_options(${TARGET} PUBLIC -Wcast-function-type -Wstrict-overflow) - endif() - - # GCC 6 - if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) - target_compile_options(${TARGET} PUBLIC -Wno-strict-aliasing) - endif() - endif() -endfunction() - #---------------------------------- # Flashing target #---------------------------------- diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index 989140e02..35e5fc923 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -92,7 +92,7 @@ static uart_inst_t *uart_inst; // // This doesn't work if others are trying to access flash at the same time, // e.g. XIP streamer, or the other core. -bool __no_inline_not_in_flash_func(get_bootsel_button)(void) { +static bool __no_inline_not_in_flash_func(get_bootsel_button)(void) { const uint CS_PIN_INDEX = 1; // Must disable interrupts, as interrupt handlers may be in flash, and we diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 3bec5bf70..5d6d8b40e 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -184,6 +184,43 @@ endif() #------------------------------------ # Functions #------------------------------------ +function(family_add_default_example_warnings TARGET) + # Apply warnings to all TinyUSB interface library sources as well as examples sources + # we cannot set compile options for target since it will not propagate to INTERFACE sources then picosdk files + foreach(TINYUSB_TARGET IN ITEMS tinyusb_common_base tinyusb_device_base tinyusb_host_base tinyusb_host_max3421 tinyusb_bsp) + get_target_property(TINYUSB_SOURCES ${TINYUSB_TARGET} INTERFACE_SOURCES) + set_source_files_properties(${TINYUSB_SOURCES} PROPERTIES COMPILE_OPTIONS "${WARN_FLAGS_${CMAKE_C_COMPILER_ID}}") + endforeach() + + # Also apply to example sources, but filter out any source files from lib/ (e.g. fatfs) + get_target_property(EXAMPLE_SOURCES ${TARGET} SOURCES) + set(FILTERED_SOURCES "") + foreach(SOURCE_FILE IN LISTS EXAMPLE_SOURCES) + string(FIND "${SOURCE_FILE}" "${TOP}/lib" FOUND_POS) + if(FOUND_POS EQUAL -1) + list(APPEND FILTERED_SOURCES ${SOURCE_FILE}) + endif() + endforeach() + set_source_files_properties(${FILTERED_SOURCES} PROPERTIES COMPILE_OPTIONS "${WARN_FLAGS_${CMAKE_C_COMPILER_ID}}") + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED) + target_link_options(${TARGET} PRIVATE "LINKER:--no-warn-rwx-segments") + endif() + + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) + target_compile_options(${TARGET} PRIVATE -Wconversion) + endif() + + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) + target_compile_options(${TARGET} PRIVATE -Wcast-function-type -Wstrict-overflow) + endif() + + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) + target_compile_options(${TARGET} PRIVATE -Wno-strict-aliasing) + endif() + endif() +endfunction() function(family_configure_target TARGET RTOS) if (RTOS STREQUAL noos OR RTOS STREQUAL "") @@ -204,7 +241,7 @@ function(family_configure_target TARGET RTOS) pico_enable_stdio_uart(${TARGET} 1) target_link_libraries(${TARGET} PUBLIC pico_stdlib tinyusb_board${RTOS_SUFFIX} tinyusb_additions) - family_flash_openocd(${TARGET}) + family_flash_openocd(${TARGET}) family_flash_jlink(${TARGET}) endfunction() @@ -359,34 +396,9 @@ function(suppress_tinyusb_warnings) ${PICO_TINYUSB_PATH}/src/portable/raspberrypi/rp2040/hcd_rp2040.c ) foreach(SOURCE_FILE IN LISTS CONVERSION_WARNING_FILES) - set_source_files_properties( - ${SOURCE_FILE} - PROPERTIES - COMPILE_FLAGS "-Wno-conversion") + set_source_files_properties(${SOURCE_FILE} PROPERTIES COMPILE_FLAGS "-Wno-conversion") endforeach() endif() - if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) - set_source_files_properties( - ${PICO_TINYUSB_PATH}/lib/fatfs/source/ff.c - COMPILE_FLAGS "-Wno-stringop-overflow -Wno-array-bounds") - endif() - set_source_files_properties( - ${PICO_TINYUSB_PATH}/lib/fatfs/source/ff.c - PROPERTIES - COMPILE_FLAGS "-Wno-conversion -Wno-cast-qual") - - set_source_files_properties( - ${PICO_TINYUSB_PATH}/lib/lwip/src/core/tcp_in.c - ${PICO_TINYUSB_PATH}/lib/lwip/src/core/tcp_out.c - PROPERTIES - COMPILE_FLAGS "-Wno-conversion") - - set_source_files_properties( - ${PICO_TINYUSB_PATH}/lib/networking/dnserver.c - ${PICO_TINYUSB_PATH}/lib/networking/dhserver.c - ${PICO_TINYUSB_PATH}/lib/networking/rndis_reports.c - PROPERTIES - COMPILE_FLAGS "-Wno-conversion -Wno-sign-conversion") if (TARGET tinyusb_pico_pio_usb) set_source_files_properties( diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c index d59a2b4ee..90eb920e0 100644 --- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c +++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c @@ -29,9 +29,20 @@ #if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && CFG_TUH_RPI_PIO_USB #include "pico.h" + #include "pio_usb.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + #include "pio_usb_ll.h" +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + //--------------------------------------------------------------------+ // INCLUDE //--------------------------------------------------------------------+ -- cgit v1.3.1 From 6b28a4478c9f8fa3993d593360eb5574bbdeda22 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 15 Nov 2025 19:46:14 +0700 Subject: make TUP_DCD_EDPT_ISO_ALLOC i.e dcd_edpt_iso_alloc()/dcd_edpt_iso_activate() as default driver implementation. dcd_edpt_close() is deprecated and will be removed from all driver in the future. --- docs/porting.rst | 10 + src/common/tusb_mcu.h | 335 ++++++++++++----------- src/device/dcd.h | 11 +- src/portable/bridgetek/ft9xx/dcd_ft9xx.c | 15 +- src/portable/chipidea/ci_fs/dcd_ci_fs.c | 18 +- src/portable/chipidea/ci_hs/dcd_ci_hs.c | 196 +++++++------ src/portable/dialog/da146xx/dcd_da146xx.c | 16 +- src/portable/microchip/pic/dcd_pic.c | 16 +- src/portable/microchip/pic32mz/dcd_pic32mz.c | 13 +- src/portable/microchip/samd/dcd_samd.c | 11 +- src/portable/microchip/samg/dcd_samg.c | 15 +- src/portable/microchip/samx7x/dcd_samx7x.c | 17 +- src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c | 299 ++++++++++---------- src/portable/nordic/nrf5x/dcd_nrf5x.c | 16 +- src/portable/nxp/khci/dcd_khci.c | 16 +- src/portable/nxp/lpc17_40/dcd_lpc17_40.c | 15 +- src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c | 16 +- src/portable/raspberrypi/pio_usb/dcd_pio_usb.c | 14 +- src/portable/renesas/rusb2/dcd_rusb2.c | 16 +- src/portable/sunxi/dcd_sunxi_musb.c | 16 +- src/portable/ti/msp430x5xx/dcd_msp430x5xx.c | 16 +- src/portable/valentyusb/eptri/dcd_eptri.c | 15 +- src/portable/wch/dcd_ch32_usbfs.c | 13 +- src/portable/wch/dcd_ch32_usbhs.c | 27 +- 24 files changed, 702 insertions(+), 450 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/docs/porting.rst b/docs/porting.rst index c3076354c..9b60ec5a2 100644 --- a/docs/porting.rst +++ b/docs/porting.rst @@ -173,12 +173,22 @@ Also make sure to enable endpoint specific interrupts. ``dcd_edpt_close()`` """""""""""""""""""" +.. warning:: + This function is deprecated, ISO transfer should implement dcd_edpt_iso_alloc() and dcd_edpt_iso_activate() instead. + Close an endpoint. his function is used for implementing alternate settings. After calling this, the device should not respond to any packets directed towards this endpoint. When called, this function must abort any transfers in progress through this endpoint, before returning. Implementation is optional. Must be called from the USB task. Interrupts could be disabled or enabled during the call. +``dcd_edpt_iso_alloc() / dcd_edpt_iso_activate()`` +"""""""""""""""""""""""""""""""""""""""""""""""""" + +dcd_edpt_iso_alloc() is used to allocate largest buffer (for all alternative interfaces) for ISO endpoints when device is enumerated. This allows DCD to allocate necessary resources for ISO endpoints in the future. + +dcd_edpt_iso_activate() is used to activate or deactivate ISO endpoint when alternate setting is set with active max packet size. + ``dcd_edpt_xfer()`` """"""""""""""""""" diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index fd922ee56..05ae6929c 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -37,14 +37,14 @@ #ifdef __ARM_ARCH // ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access #if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1 - #define TUP_ARCH_STRICT_ALIGN 0 + #define TUP_ARCH_STRICT_ALIGN 0 #else - #define TUP_ARCH_STRICT_ALIGN 1 + #define TUP_ARCH_STRICT_ALIGN 1 #endif #else // TODO default to strict align for others // Should investigate other architecture such as risv, xtensa, mips for optimal setting - #define TUP_ARCH_STRICT_ALIGN 1 + #define TUP_ARCH_STRICT_ALIGN 1 #endif /* USB Controller Attributes for Device, Host or MCU (both) @@ -57,41 +57,41 @@ //--------------------------------------------------------------------+ // NXP //--------------------------------------------------------------------+ -#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) +#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) #define TUP_USBIP_IP3511 - #define TUP_DCD_ENDPOINT_MAX 5 + #define TUP_DCD_ENDPOINT_MAX 5 #elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX) - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 #define TUP_USBIP_OHCI #define TUP_USBIP_OHCI_NXP - #define TUP_OHCI_RHPORTS 2 + #define TUP_OHCI_RHPORTS 2 #elif TU_CHECK_MCU(OPT_MCU_LPC51UXX) - #define TUP_USBIP_IP3511 - #define TUP_DCD_ENDPOINT_MAX 5 + #define TUP_USBIP_IP3511 + #define TUP_DCD_ENDPOINT_MAX 5 #elif TU_CHECK_MCU(OPT_MCU_LPC54) // TODO USB0 has 5, USB1 has 6 #define TUP_USBIP_IP3511 - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #elif TU_CHECK_MCU(OPT_MCU_LPC55) // TODO USB0 has 5, USB1 has 6 #define TUP_USBIP_IP3511 #define TUP_USBIP_OHCI #define TUP_USBIP_OHCI_NXP - #define TUP_OHCI_RHPORTS 1 // 1 downstream port + #define TUP_OHCI_RHPORTS 1 // 1 downstream port - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) // USB0 has 6 with HS PHY, USB1 has 4 only FS #define TUP_USBIP_CHIPIDEA_HS #define TUP_USBIP_EHCI - #define TUP_DCD_ENDPOINT_MAX 6 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_RHPORT_HIGHSPEED 1 #elif TU_CHECK_MCU(OPT_MCU_MCXN9) // USB0 is chipidea FS @@ -102,15 +102,15 @@ #define TUP_USBIP_CHIPIDEA_HS #define TUP_USBIP_EHCI - #define TUP_DCD_ENDPOINT_MAX 8 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 #elif TU_CHECK_MCU(OPT_MCU_MCXA15) // USB0 is chipidea FS #define TUP_USBIP_CHIPIDEA_FS #define TUP_USBIP_CHIPIDEA_FS_MCX - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 #elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) #include "fsl_device_registers.h" @@ -118,60 +118,61 @@ #define TUP_USBIP_CHIPIDEA_HS #define TUP_USBIP_EHCI - #define TUP_DCD_ENDPOINT_MAX 8 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 #if __CORTEX_M == 7 - #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 1 - #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 1 - #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 + #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 1 + #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 1 + #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 #endif #elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K) #define TUP_USBIP_CHIPIDEA_FS #define TUP_USBIP_CHIPIDEA_FS_KINETIS - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 #elif TU_CHECK_MCU(OPT_MCU_MM32F327X) - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_EDPT_CLOSE_API //--------------------------------------------------------------------+ // Nordic //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_NRF5X) // 8 CBI + 1 ISO - #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_DCD_EDPT_CLOSE_API #elif TU_CHECK_MCU(OPT_MCU_NRF54) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_NRF - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 //--------------------------------------------------------------------+ // Microchip //--------------------------------------------------------------------+ -#elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML2X, OPT_MCU_SAMD21) || \ - TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) - #define TUP_DCD_ENDPOINT_MAX 8 +#elif TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML2X, OPT_MCU_SAMD21) || TU_CHECK_MCU(OPT_MCU_SAMD51, OPT_MCU_SAME5X) + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_SAMG) - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY #elif TU_CHECK_MCU(OPT_MCU_SAMX7X) - #define TUP_DCD_ENDPOINT_MAX 10 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 10 + #define TUP_RHPORT_HIGHSPEED 1 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY #elif TU_CHECK_MCU(OPT_MCU_PIC32MZ) - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY -#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \ - TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) - #define TUP_DCD_ENDPOINT_MAX 16 +#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) + #define TUP_DCD_ENDPOINT_MAX 16 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define TUP_DCD_EDPT_CLOSE_API //--------------------------------------------------------------------+ // ST @@ -179,23 +180,23 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32F0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32F1) // - F102, F103 use fsdev // - F105, F107 use dwc2 - #if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \ - defined (STM32F107xB) || defined (STM32F107xC) + #if defined(STM32F105x8) || defined(STM32F105xB) || defined(STM32F105xC) || defined(STM32F107xB) || \ + defined(STM32F107xC) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 - #define TUP_DCD_ENDPOINT_MAX 4 - #elif defined(STM32F102x6) || defined(STM32F102xB) || \ - defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) + #define TUP_DCD_ENDPOINT_MAX 4 + #elif defined(STM32F102x6) || defined(STM32F102xB) || defined(STM32F103x6) || defined(STM32F103xB) || \ + defined(STM32F103xE) || defined(STM32F103xG) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #else #error "Unsupported STM32F1 mcu" #endif @@ -205,55 +206,55 @@ #define TUP_USBIP_DWC2_STM32 // FS has 4 ep, HS has 5 ep - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #elif TU_CHECK_MCU(OPT_MCU_STM32F3) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32F4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 // For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9 - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #elif TU_CHECK_MCU(OPT_MCU_STM32F7) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 // FS has 6, HS has 9 - #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_DCD_ENDPOINT_MAX 9 // MCU with on-chip HS Phy #if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx) - #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS + #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS #endif // Enable dcache if DMA is enabled - #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE - #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE - #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 + #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 #elif TU_CHECK_MCU(OPT_MCU_STM32H7) #include "stm32h7xx.h" #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 - #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_DCD_ENDPOINT_MAX 9 #if __CORTEX_M == 7 // Enable dcache if DMA is enabled - #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE - #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE - #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 + #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32 #endif #elif TU_CHECK_MCU(OPT_MCU_STM32H5) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32G4) // Device controller @@ -262,41 +263,40 @@ // TypeC controller #define TUP_USBIP_TYPEC_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #define TUP_TYPEC_RHPORTS_NUM 1 #elif TU_CHECK_MCU(OPT_MCU_STM32G0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32C0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32L4) // - L4x2, L4x3 use fsdev // - L4x4, L4x6, L4x7, L4x9 use dwc2 - #if defined (STM32L475xx) || defined (STM32L476xx) || \ - defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \ - defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \ - defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \ - defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx) + #if defined(STM32L475xx) || defined(STM32L476xx) || defined(STM32L485xx) || defined(STM32L486xx) || \ + defined(STM32L496xx) || defined(STM32L4A6xx) || defined(STM32L4P5xx) || defined(STM32L4Q5xx) || \ + defined(STM32L4R5xx) || defined(STM32L4R7xx) || defined(STM32L4R9xx) || defined(STM32L4S5xx) || \ + defined(STM32L4S7xx) || defined(STM32L4S9xx) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \ - defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx) + defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #else #error "Unsupported STM32L4 mcu" #endif @@ -304,19 +304,19 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32WB) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32WBA) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 - #define TUP_DCD_ENDPOINT_MAX 9 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 #elif TU_CHECK_MCU(OPT_MCU_STM32U5) - #if defined (STM32U535xx) || defined (STM32U545xx) + #if defined(STM32U535xx) || defined(STM32U545xx) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #else #define TUP_USBIP_DWC2 @@ -324,38 +324,38 @@ // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \ - defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) - #define TUP_DCD_ENDPOINT_MAX 9 - #define TUP_RHPORT_HIGHSPEED 1 + defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx) + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 #else - #define TUP_DCD_ENDPOINT_MAX 6 + #define TUP_DCD_ENDPOINT_MAX 6 #endif #endif #elif TU_CHECK_MCU(OPT_MCU_STM32L5) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32U0) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32U3) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_STM32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_STM32H7RS, OPT_MCU_STM32N6) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 // FS has 6, HS has 9 - #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_DCD_ENDPOINT_MAX 9 // MCU with on-chip HS Phy - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_RHPORT_HIGHSPEED 1 // Enable dcache if DMA is enabled #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE @@ -366,43 +366,39 @@ // Sony //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_CXD56) - #define TUP_DCD_ENDPOINT_MAX 7 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 7 + #define TUP_RHPORT_HIGHSPEED 1 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY - #define TUP_DCD_EDPT_ISO_ALLOC //--------------------------------------------------------------------+ // TI //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx) - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129) #define TUP_USBIP_MUSB #define TUP_USBIP_MUSB_TI - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 //--------------------------------------------------------------------+ // ValentyUSB (Litex) //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI) - #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_DCD_ENDPOINT_MAX 16 //--------------------------------------------------------------------+ // Nuvoton //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126) - #define TUP_DCD_ENDPOINT_MAX 8 - #define TUP_DCD_EDPT_ISO_ALLOC + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_NUC120) - #define TUP_DCD_ENDPOINT_MAX 6 - #define TUP_DCD_EDPT_ISO_ALLOC + #define TUP_DCD_ENDPOINT_MAX 6 #elif TU_CHECK_MCU(OPT_MCU_NUC505) - #define TUP_DCD_ENDPOINT_MAX 12 - #define TUP_RHPORT_HIGHSPEED 1 - #define TUP_DCD_EDPT_ISO_ALLOC + #define TUP_DCD_ENDPOINT_MAX 12 + #define TUP_RHPORT_HIGHSPEED 1 //--------------------------------------------------------------------+ // Espressif @@ -410,114 +406,124 @@ #elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_ESP32 - #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN - #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN + + // clang-format off + #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + // clang-format on #if CFG_TUSB_MCU == OPT_MCU_ESP32S3 #define TUP_MCU_MULTIPLE_CORE 1 #endif // Disable slave if DMA is enabled - #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE - #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE #elif TU_CHECK_MCU(OPT_MCU_ESP32P4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_ESP32 - #define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS - #define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep + #define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS + #define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep - #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + // clang-format off + #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + // clang-format on - #define TUP_MCU_MULTIPLE_CORE 1 + #define TUP_MCU_MULTIPLE_CORE 1 // Disable slave if DMA is enabled - #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE - #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE // Enable dcache if DMA is enabled - #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE - #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE - #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64 + #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64 -#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C5, OPT_MCU_ESP32C6, OPT_MCU_ESP32C61, OPT_MCU_ESP32H2) +#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C5, OPT_MCU_ESP32C6, \ + OPT_MCU_ESP32C61, OPT_MCU_ESP32H2) #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) - #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" + #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" #endif - #define TUP_DCD_ENDPOINT_MAX 0 - #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + #define TUP_DCD_ENDPOINT_MAX 0 + + // clang-format off + #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + // clang-format on + //--------------------------------------------------------------------+ // Dialog //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_DA1469X) - #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_EDPT_CLOSE_API //--------------------------------------------------------------------+ // Raspberry Pi //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_RP2040) - #define TUP_DCD_EDPT_ISO_ALLOC - #define TUP_DCD_ENDPOINT_MAX 16 - #define TUP_MCU_MULTIPLE_CORE 1 + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_MCU_MULTIPLE_CORE 1 - #define TU_ATTR_FAST_FUNC __not_in_flash("tinyusb") + #define TU_ATTR_FAST_FUNC __not_in_flash("tinyusb") //--------------------------------------------------------------------+ // Silabs //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_EFM32GG) #define TUP_USBIP_DWC2 - #define TUP_DCD_ENDPOINT_MAX 7 + #define TUP_DCD_ENDPOINT_MAX 7 //--------------------------------------------------------------------+ // Renesas //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX) #define TUP_USBIP_RUSB2 - #define TUP_DCD_ENDPOINT_MAX 10 + #define TUP_DCD_ENDPOINT_MAX 10 //--------------------------------------------------------------------+ // GigaDevice //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_GD32VF103) #define TUP_USBIP_DWC2 - #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_ENDPOINT_MAX 4 //--------------------------------------------------------------------+ // Broadcom //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) #define TUP_USBIP_DWC2 - #define TUP_DCD_ENDPOINT_MAX 8 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 //--------------------------------------------------------------------+ // Infineon //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_XMC4000) #define TUP_USBIP_DWC2 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 //--------------------------------------------------------------------+ // BridgeTek //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_FT90X) - #define TUP_DCD_ENDPOINT_MAX 8 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_RHPORT_HIGHSPEED 1 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY #elif TU_CHECK_MCU(OPT_MCU_FT93X) - #define TUP_DCD_ENDPOINT_MAX 16 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 16 + #define TUP_RHPORT_HIGHSPEED 1 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY //--------------------------------------------------------------------+ // Allwinner //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_F1C100S) - #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_ENDPOINT_MAX 4 //--------------------------------------------------------------------+ // WCH @@ -527,31 +533,31 @@ #define TUP_USBIP_WCH_USBFS #if !defined(CFG_TUD_WCH_USBIP_USBFS) - #define CFG_TUD_WCH_USBIP_USBFS 0 + #define CFG_TUD_WCH_USBIP_USBFS 0 #endif #if !defined(CFG_TUD_WCH_USBIP_USBHS) - #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) + #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) #endif - #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS - #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8) + #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS + #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8) #elif TU_CHECK_MCU(OPT_MCU_CH32V103) #define TUP_USBIP_WCH_USBFS #if !defined(CFG_TUD_WCH_USBIP_USBFS) - #define CFG_TUD_WCH_USBIP_USBFS 1 + #define CFG_TUD_WCH_USBIP_USBFS 1 #endif - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_CH32V20X) // v20x support both port0 FSDEV (USBD) and port1 USBFS #define TUP_USBIP_WCH_USBFS #ifndef CFG_TUH_WCH_USBIP_USBFS - #define CFG_TUH_WCH_USBIP_USBFS 1 + #define CFG_TUH_WCH_USBIP_USBFS 1 #endif #define TUP_USBIP_FSDEV @@ -559,14 +565,14 @@ // default to FSDEV for device #if !defined(CFG_TUD_WCH_USBIP_USBFS) - #define CFG_TUD_WCH_USBIP_USBFS 0 + #define CFG_TUD_WCH_USBIP_USBFS 0 #endif #if !defined(CFG_TUD_WCH_USBIP_FSDEV) - #define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) + #define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) #endif - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_CH32V307) // v307 support both FS and HS, default to HS @@ -574,15 +580,15 @@ #define TUP_USBIP_WCH_USBFS #if !defined(CFG_TUD_WCH_USBIP_USBFS) - #define CFG_TUD_WCH_USBIP_USBFS 0 + #define CFG_TUD_WCH_USBIP_USBFS 0 #endif #if !defined(CFG_TUD_WCH_USBIP_USBHS) - #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) + #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1) #endif - #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS - #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8) + #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS + #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8) //--------------------------------------------------------------------+ // Analog Devices @@ -590,8 +596,8 @@ #elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) #define TUP_USBIP_MUSB #define TUP_USBIP_MUSB_ADI - #define TUP_DCD_ENDPOINT_MAX 12 - #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 12 + #define TUP_RHPORT_HIGHSPEED 1 #define TUD_ENDPOINT_ONE_DIRECTION_ONLY //--------------------------------------------------------------------+ @@ -600,46 +606,44 @@ #elif TU_CHECK_MCU(OPT_MCU_AT32F403A_407) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_AT32F413) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_AT32F415) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_AT32 - #define TUP_DCD_ENDPOINT_MAX 4 + #define TUP_DCD_ENDPOINT_MAX 4 #elif TU_CHECK_MCU(OPT_MCU_AT32F435_437) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_AT32F423) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #elif TU_CHECK_MCU(OPT_MCU_AT32F402_405) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 // AT32F405xx has on-chip HS PHY - #if defined(AT32F405CBT7) || defined(AT32F405CBU7) || \ - defined(AT32F405CCT7) || defined(AT32F405CCU7) || \ - defined(AT32F405KBU7_4) || defined(AT32F405KCU7_4) || \ - defined(AT32F405RBT7_7) || defined(AT32F405RBT7) || \ - defined(AT32F405RCT7_7) || defined(AT32F405RCT7) - #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS + #if defined(AT32F405CBT7) || defined(AT32F405CBU7) || defined(AT32F405CCT7) || defined(AT32F405CCU7) || \ + defined(AT32F405KBU7_4) || defined(AT32F405KCU7_4) || defined(AT32F405RBT7_7) || defined(AT32F405RBT7) || \ + defined(AT32F405RCT7_7) || defined(AT32F405RCT7) + #define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS #endif #elif TU_CHECK_MCU(OPT_MCU_AT32F425) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_AT32 - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #endif @@ -649,7 +653,7 @@ #if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 #ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL - #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1)) + #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4 * (CFG_TUH_DEVICE_MAX - 1)) #endif #endif @@ -659,17 +663,17 @@ //--------------------------------------------------------------------+ #ifndef TUP_MCU_MULTIPLE_CORE -#define TUP_MCU_MULTIPLE_CORE 0 + #define TUP_MCU_MULTIPLE_CORE 0 #endif #if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED #warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8" - #define TUP_DCD_ENDPOINT_MAX 8 + #define TUP_DCD_ENDPOINT_MAX 8 #endif // Default to fullspeed if not defined #ifndef TUP_RHPORT_HIGHSPEED - #define TUP_RHPORT_HIGHSPEED 0 + #define TUP_RHPORT_HIGHSPEED 0 #endif // fast function, normally mean placing function in SRAM @@ -677,8 +681,13 @@ #define TU_ATTR_FAST_FUNC #endif -// USBIP that support ISO alloc & activate API -#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) || defined(TUP_USBIP_MUSB) +#if defined(TUP_USBIP_CHIPIDEA_FS) || defined(TUP_USBIP_IP3511) || defined(TUP_USBIP_RUSB2) || \ + (defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS) + #define TUP_DCD_EDPT_CLOSE_API +#endif + +// USBIP implement dcd_edpt_close() and does not support ISO alloc & activate API +#ifndef TUP_DCD_EDPT_CLOSE_API #define TUP_DCD_EDPT_ISO_ALLOC #endif diff --git a/src/device/dcd.h b/src/device/dcd.h index 1b0289280..353f836ee 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -171,7 +171,12 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); // This API never calls with control endpoints, since it is auto cleared when receiving setup packet void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr); -#ifdef TUP_DCD_EDPT_ISO_ALLOC +#ifdef TUP_DCD_EDPT_CLOSE_API +// Close an endpoint. +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr); + +#else + // Allocate packet buffer used by ISO endpoints // Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size); @@ -179,10 +184,6 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet // Configure and enable an ISO endpoint according to descriptor bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep); -#else -// Close an endpoint. -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr); - #endif //--------------------------------------------------------------------+ diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c index 34a8be3b6..6ed4be410 100644 --- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c +++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c @@ -806,6 +806,20 @@ void dcd_edpt_close_all(uint8_t rhport) _ft9xx_reset_edpts(); } +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} + + // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { @@ -1200,5 +1214,4 @@ void ft9xx_usbd_pm_ISR(void) } } } - #endif diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c index 11ddb683f..8b5c42aa2 100644 --- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c +++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c @@ -399,6 +399,7 @@ void dcd_edpt_close_all(uint8_t rhport) } } +#ifdef TUP_DCD_EDPT_CLOSE_API void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { const unsigned epn = tu_edpt_number(ep_addr); @@ -419,6 +420,22 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) dcd_int_enable(rhport); } +#else + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + (void) ep_addr; + (void) largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; +} +#endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { const unsigned epn = tu_edpt_number(ep_addr); @@ -564,5 +581,4 @@ void dcd_int_handler(uint8_t rhport) process_tokdne(rhport); } } - #endif diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c index 244f5a2d4..8c5253eb9 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -64,16 +64,19 @@ bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { //--------------------------------------------------------------------+ // ENDPTCTRL +enum { + ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field +}; + enum { ENDPTCTRL_STALL = TU_BIT(0), ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only ENDPTCTRL_TOGGLE_RESET = TU_BIT(6), - ENDPTCTRL_ENABLE = TU_BIT(7) + ENDPTCTRL_ENABLE = TU_BIT(7), }; -enum { - ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field -}; +#define ENDPTCTRL_TYPE(_type) ((_type) << ENDPTCTRL_TYPE_POS) + #define ENDPTCTRL_RESET_MASK (ENDPTCTRL_TYPE(TUSB_XFER_BULK) | (ENDPTCTRL_TYPE(TUSB_XFER_BULK) << 16u)) // USBSTS, USBINTR enum { @@ -170,9 +173,7 @@ static dcd_data_t _dcd_data; // Prototypes and Helper Functions //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE -static inline uint8_t ci_ep_count(ci_hs_regs_t const* dcd_reg) -{ +TU_ATTR_ALWAYS_INLINE static inline uint8_t ci_ep_count(const ci_hs_regs_t *dcd_reg) { return dcd_reg->DCCPARAMS & DCCPARAMS_DEN_MASK; } @@ -191,9 +192,8 @@ static void bus_reset(uint8_t rhport) // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior // for the data PID tracking on the active endpoint. uint8_t const ep_count = ci_ep_count(dcd_reg); - for( uint8_t i=1; i < ep_count; i++) - { - dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS)); + for (uint8_t i = 1; i < ep_count; i++) { + dcd_reg->ENDPTCTRL[i] = ENDPTCTRL_RESET_MASK; } //------------- Clear All Registers -------------// @@ -315,26 +315,25 @@ void dcd_sof_enable(uint8_t rhport, bool en) // HELPER //--------------------------------------------------------------------+ -static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) -{ - dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes); +static void qtd_init(dcd_qtd_t *p_qtd, void *data_ptr, uint16_t total_bytes) { + dcd_dcache_clean_invalidate((uint32_t *)tu_align((uint32_t)data_ptr, 4), total_bytes); tu_memclr(p_qtd, sizeof(dcd_qtd_t)); - p_qtd->next = QTD_NEXT_INVALID; - p_qtd->active = 1; - p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; - p_qtd->int_on_complete = true; + p_qtd->next = QTD_NEXT_INVALID; + p_qtd->active = 1; + p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes; + p_qtd->int_on_complete = true; - if (data_ptr != NULL) - { - p_qtd->buffer[0] = (uint32_t) data_ptr; + if (data_ptr != NULL) { + p_qtd->buffer[0] = (uint32_t)data_ptr; - uint32_t const bufend = p_qtd->buffer[0] + total_bytes; - for(uint8_t i=1; i<5; i++) - { - uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096; - if ( bufend <= next_page ) break; + const uint32_t bufend = p_qtd->buffer[0] + total_bytes; + for (uint8_t i = 1; i < 5; i++) { + const uint32_t next_page = tu_align4k(p_qtd->buffer[i - 1]) + 4096; + if (bufend <= next_page) { + break; + } p_qtd->buffer[i] = next_page; @@ -346,6 +345,35 @@ static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) //--------------------------------------------------------------------+ // DCD Endpoint Port //--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_write(volatile uint32_t *epctrl, uint8_t dir, uint32_t value) { + if (dir == TUSB_DIR_OUT) { + *epctrl = (*epctrl & 0xFFFF0000u) | value; + } else { + *epctrl = (*epctrl & 0x0000FFFFu) | (value << 16); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_mask(volatile uint32_t *epctrl, uint8_t dir, uint32_t and_mask, + uint32_t or_mask) { + uint32_t value = *epctrl; + if (and_mask != 0) { + value &= (dir == TUSB_DIR_OUT) ? and_mask : (and_mask << 16u); + } + if (or_mask != 0) { + value |= (dir == TUSB_DIR_OUT) ? or_mask : (or_mask << 16u); + } + + *epctrl = value; +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_set(volatile uint32_t *epctrl, uint8_t dir, uint32_t mask) { + ep_ctrl_mask(epctrl, dir, 0, mask); +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_ctrl_clear(volatile uint32_t *epctrl, uint8_t dir, uint32_t mask) { + ep_ctrl_mask(epctrl, dir, ~mask, 0); +} + void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { uint8_t const epnum = tu_edpt_number(ep_addr); @@ -366,80 +394,91 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) // data toggle also need to be reset ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport); dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 ); - dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0)); + dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << (dir ? 16 : 0)); } -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ - uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - - ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport); - - // Must not exceed max endpoint number - TU_ASSERT(epnum < ci_ep_count(dcd_reg)); - - //------------- Prepare Queue Head -------------// - dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir]; +static void qhd_init(dcd_qhd_t *p_qhd, uint16_t max_packet_size, uint8_t iso_mult) { tu_memclr(p_qhd, sizeof(dcd_qhd_t)); - p_qhd->zero_length_termination = 1; - p_qhd->max_packet_size = tu_edpt_packet_size(p_endpoint_desc); - if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) - { - p_qhd->iso_mult = 1; - } - + p_qhd->max_packet_size = max_packet_size; + p_qhd->iso_mult = iso_mult; p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); +} - // Enable EP Control - uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET; +bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *endpoint_desc) { + ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport); + const uint8_t epnum = tu_edpt_number(endpoint_desc->bEndpointAddress); + const uint8_t dir = tu_edpt_dir(endpoint_desc->bEndpointAddress); + const uint8_t xfer_type = endpoint_desc->bmAttributes.xfer; + TU_ASSERT(epnum < ci_ep_count(dcd_reg)); - if ( dir == TUSB_DIR_OUT ) - { - dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl; - }else - { - dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16); - } + dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir]; + qhd_init(p_qhd, tu_edpt_packet_size(endpoint_desc), 0u); + + // EP Control + const uint32_t epctrl = ENDPTCTRL_TYPE(xfer_type) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET; + ep_ctrl_write(&dcd_reg->ENDPTCTRL[epnum], dir, epctrl); return true; } -void dcd_edpt_close_all (uint8_t rhport) -{ - ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport); +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; - // Disable all non-control endpoints - uint8_t const ep_count = ci_ep_count(dcd_reg); - for (uint8_t epnum = 1; epnum < ep_count; epnum++) - { - _dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1; - _dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1; + ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport); + const uint8_t epnum = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); + TU_ASSERT(epnum < ci_ep_count(dcd_reg)); - dcd_reg->ENDPTFLUSH = TU_BIT(epnum) | TU_BIT(epnum+16); - dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS)); - } + // EP Control: set type but not enabled yet + const uint32_t epctrl = ENDPTCTRL_TYPE(TUSB_XFER_ISOCHRONOUS) | ENDPTCTRL_TOGGLE_RESET; + ep_ctrl_write(&dcd_reg->ENDPTCTRL[epnum], dir, epctrl); + + return true; } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + const uint8_t epnum = tu_edpt_number(desc_ep->bEndpointAddress); + const uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress); + ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport); + TU_ASSERT(epnum < ci_ep_count(dcd_reg)); - ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport); + dcd_qhd_t *p_qhd = &_dcd_data.qhd[epnum][dir]; + volatile uint32_t *endptctrl = &dcd_reg->ENDPTCTRL[epnum]; - _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1; + // _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1; + // dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); // Flush EP - uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0)); - dcd_reg->ENDPTFLUSH = flush_mask; - while(dcd_reg->ENDPTFLUSH & flush_mask); + const uint32_t flush_mask = TU_BIT(epnum + (dir ? 16 : 0)); + dcd_reg->ENDPTFLUSH = flush_mask; + while (dcd_reg->ENDPTFLUSH & flush_mask) {} + + // disable to change max packet size + ep_ctrl_clear(endptctrl, dir, ENDPTCTRL_ENABLE); - // Clear EP enable - dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0)); + qhd_init(p_qhd, tu_edpt_packet_size(desc_ep), 1u); + + ep_ctrl_set(endptctrl, dir, ENDPTCTRL_ENABLE); + + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) { + ci_hs_regs_t *dcd_reg = CI_HS_REG(rhport); + + // Disable all non-control endpoints + uint8_t const ep_count = ci_ep_count(dcd_reg); + for (uint8_t epnum = 1; epnum < ep_count; epnum++) { + _dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1; + _dcd_data.qhd[epnum][TUSB_DIR_IN].qtd_overlay.halted = 1; + + dcd_reg->ENDPTFLUSH = TU_BIT(epnum) | TU_BIT(epnum + 16); + dcd_reg->ENDPTCTRL[epnum] = ENDPTCTRL_RESET_MASK; + } } static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir) @@ -678,5 +717,4 @@ void dcd_int_handler(uint8_t rhport) dcd_event_sof(rhport, frame, true); } } - #endif diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 56ecb7575..868a10dd9 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -1025,6 +1025,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) tu_memclr(xfer, sizeof(*xfer)); } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + (void) ep_addr; + (void) largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; +} +#endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { uint8_t const epnum = tu_edpt_number(ep_addr); @@ -1218,5 +1233,4 @@ void dcd_int_handler(uint8_t rhport) handle_alt_ev(); } } - #endif diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c index b4a698199..8413a7ddd 100644 --- a/src/portable/microchip/pic/dcd_pic.c +++ b/src/portable/microchip/pic/dcd_pic.c @@ -687,6 +687,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) if (ie) intr_enable(rhport); } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + (void) ep_addr; + (void) largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; +} +#endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { @@ -866,5 +881,4 @@ void dcd_int_handler(uint8_t rhport) intr_clear(rhport); } - #endif diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c index cbd157d6b..b039bb505 100644 --- a/src/portable/microchip/pic32mz/dcd_pic32mz.c +++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c @@ -438,12 +438,20 @@ void dcd_edpt_close_all (uint8_t rhport) } } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) -{ +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void) rhport; (void) ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { uint8_t const epnum = tu_edpt_number(ep_addr); @@ -744,5 +752,4 @@ void dcd_int_handler(uint8_t rhport) } } } - #endif diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index e43439f2a..383cf60e5 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -245,11 +245,17 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) return true; } -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) { +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void) rhport; (void) ep_addr; + (void)largest_packet_size; + return false; +} - // TODO: implement if necessary? +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } void dcd_edpt_close_all (uint8_t rhport) @@ -427,5 +433,4 @@ void dcd_int_handler (uint8_t rhport) // Handle complete transfer maybe_transfer_complete(); } - #endif diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index a5c768839..879b2eb2b 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -270,9 +270,17 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) return true; } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; (void) ep_addr; - // TODO implement dcd_edpt_close() +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } void dcd_edpt_close_all (uint8_t rhport) @@ -494,5 +502,4 @@ void dcd_int_handler(uint8_t rhport) } } } - #endif diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c index 8aec1568d..51308f7a2 100644 --- a/src/portable/microchip/samx7x/dcd_samx7x.c +++ b/src/portable/microchip/samx7x/dcd_samx7x.c @@ -559,15 +559,17 @@ void dcd_edpt_close_all (uint8_t rhport) // TODO implement dcd_edpt_close_all() } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) -{ +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void) rhport; - uint8_t const epnum = tu_edpt_number(ep_addr); + (void) ep_addr; + (void) largest_packet_size; + return false; +} - // Disable endpoint interrupt - USB_REG->DEVIDR = 1 << (DEVIDR_PEP_0_Pos + epnum); - // Disable EP - USB_REG->DEVEPT &=~(1 << (DEVEPT_EPEN0_Pos + epnum)); +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; } static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix) @@ -773,5 +775,4 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) USB_REG->DEVEPTIDR[epnum] = DEVEPTIDR_CTRL_STALLRQC; USB_REG->DEVEPTIER[epnum] = HSTPIPIER_RSTDTS; } - #endif diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c index 1ce3da27e..a232810a2 100644 --- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c +++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c @@ -26,7 +26,7 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MM32F327X ) +#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_MM32F327X) #include "reg_usb_otg_fs.h" #include "mm32_device.h" @@ -43,56 +43,53 @@ enum { TOK_PID_SETUP = 0xDu, }; -typedef struct TU_ATTR_PACKED -{ +typedef struct TU_ATTR_PACKED { union { uint32_t head; struct { union { struct { - uint16_t : 2; - uint16_t tok_pid : 4; - uint16_t data : 1; - uint16_t own : 1; - uint16_t : 8; + uint16_t : 2; + uint16_t tok_pid : 4; + uint16_t data : 1; + uint16_t own : 1; + uint16_t : 8; }; struct { - uint16_t : 2; - uint16_t bdt_stall: 1; - uint16_t dts : 1; - uint16_t ninc : 1; - uint16_t keep : 1; - uint16_t : 10; + uint16_t : 2; + uint16_t bdt_stall : 1; + uint16_t dts : 1; + uint16_t ninc : 1; + uint16_t keep : 1; + uint16_t : 10; }; }; - uint16_t bc : 10; - uint16_t : 6; + uint16_t bc : 10; + uint16_t : 6; }; }; uint8_t *addr; -}buffer_descriptor_t; +} buffer_descriptor_t; -TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(buffer_descriptor_t) == 8, "size is not correct"); -typedef struct TU_ATTR_PACKED -{ +typedef struct TU_ATTR_PACKED { union { uint32_t state; struct { - uint32_t max_packet_size :11; + uint32_t max_packet_size : 11; uint32_t : 5; uint32_t odd : 1; - uint32_t :15; + uint32_t : 15; }; }; uint16_t length; uint16_t remaining; -}endpoint_state_t; +} endpoint_state_t; -TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(endpoint_state_t) == 8, "size is not correct"); -typedef struct -{ +typedef struct { union { /* [#EP][OUT,IN][EVEN,ODD] */ buffer_descriptor_t bdt[16][2][2]; @@ -104,7 +101,7 @@ typedef struct }; uint8_t setup_packet[8]; uint8_t addr; -}dcd_data_t; +} dcd_data_t; //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION @@ -112,10 +109,9 @@ typedef struct // BDT(Buffer Descriptor Table) must be 256-byte aligned CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd; -TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(_dcd.bdt) == 512, "size is not correct"); -static void prepare_next_setup_packet(uint8_t rhport) -{ +static void prepare_next_setup_packet(uint8_t rhport) { const unsigned out_odd = _dcd.endpoint[0][0].odd; const unsigned in_odd = _dcd.endpoint[0][1].odd; if (_dcd.bdt[0][0][out_odd].own) { @@ -126,12 +122,10 @@ static void prepare_next_setup_packet(uint8_t rhport) _dcd.bdt[0][0][out_odd ^ 1].data = 1; _dcd.bdt[0][1][in_odd].data = 1; _dcd.bdt[0][1][in_odd ^ 1].data = 0; - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), - _dcd.setup_packet, sizeof(_dcd.setup_packet)); + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), _dcd.setup_packet, sizeof(_dcd.setup_packet)); } -static void process_stall(uint8_t rhport) -{ +static void process_stall(uint8_t rhport) { if (USB_OTG_FS->EP_CTL[0] & USB_ENDPT_EPSTALL_MASK) { /* clear stall condition of the control pipe */ prepare_next_setup_packet(rhport); @@ -139,13 +133,12 @@ static void process_stall(uint8_t rhport) } } -static void process_tokdne(uint8_t rhport) -{ - const unsigned s = USB_OTG_FS->STAT; - USB_OTG_FS->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */ - buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s]; - endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3]; - unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0; +static void process_tokdne(uint8_t rhport) { + const unsigned s = USB_OTG_FS->STAT; + USB_OTG_FS->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */ + buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s]; + endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3]; + unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0; /* fetch pid before discarded by the next steps */ const unsigned pid = bd->tok_pid; @@ -155,7 +148,7 @@ static void process_tokdne(uint8_t rhport) bd->ninc = 0; bd->keep = 0; /* update the odd variable to prepare for the next transfer */ - ep->odd = odd ^ 1; + ep->odd = odd ^ 1; if (pid == TOK_PID_SETUP) { dcd_event_setup_received(rhport, bd->addr, true); USB_OTG_FS->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK; @@ -165,25 +158,24 @@ static void process_tokdne(uint8_t rhport) TU_LOG1("TKDNE %x\r\n", s); } - const unsigned bc = bd->bc; + const unsigned bc = bd->bc; const unsigned remaining = ep->remaining - bc; if (remaining && bc == ep->max_packet_size) { /* continue the transferring consecutive data */ - ep->remaining = remaining; + ep->remaining = remaining; const int next_remaining = remaining - ep->max_packet_size; if (next_remaining > 0) { /* prepare to the after next transfer */ bd->addr += ep->max_packet_size * 2; - bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining; + bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size : next_remaining; __DSB(); - bd->own = 1; /* the own bit must set after addr */ + bd->own = 1; /* the own bit must set after addr */ } return; } const unsigned length = ep->length; - dcd_event_xfer_complete(rhport, - ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT), - length - remaining, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT), length - remaining, + XFER_RESULT_SUCCESS, true); if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) { /* After completion a ZLP of control transfer, * it prepares for the next steup transfer. */ @@ -191,24 +183,23 @@ static void process_tokdne(uint8_t rhport) /* When the transfer was the SetAddress, * the device address should be updated here. */ USB_OTG_FS->ADDR = _dcd.addr; - _dcd.addr = 0; + _dcd.addr = 0; } prepare_next_setup_packet(rhport); } } -static void process_bus_reset(uint8_t rhport) -{ - USB_OTG_FS->CTL |= USB_CTL_ODDRST_MASK; - USB_OTG_FS->ADDR = 0; - USB_OTG_FS->INT_ENB = (USB_OTG_FS->INT_ENB & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK; +static void process_bus_reset(uint8_t rhport) { + USB_OTG_FS->CTL |= USB_CTL_ODDRST_MASK; + USB_OTG_FS->ADDR = 0; + USB_OTG_FS->INT_ENB = (USB_OTG_FS->INT_ENB & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK; USB_OTG_FS->EP_CTL[0] = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK; for (unsigned i = 1; i < 16; ++i) { USB_OTG_FS->EP_CTL[i] = 0; } buffer_descriptor_t *bd = _dcd.bdt[0][0]; - for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) { + for (unsigned i = 0; i < sizeof(_dcd.bdt) / sizeof(*bd); ++i, ++bd) { bd->head = 0; } const endpoint_state_t ep0 = { @@ -226,31 +217,29 @@ static void process_bus_reset(uint8_t rhport) dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true); } -static void process_bus_inactive(uint8_t rhport) -{ - (void) rhport; +static void process_bus_inactive(uint8_t rhport) { + (void)rhport; const unsigned inten = USB_OTG_FS->INT_ENB; - USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK; + USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK; dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); } -static void process_bus_active(uint8_t rhport) -{ - (void) rhport; +static void process_bus_active(uint8_t rhport) { + (void)rhport; const unsigned inten = USB_OTG_FS->INT_ENB; - USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK; + USB_OTG_FS->INT_ENB = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK; dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); } /*------------------------------------------------------------------*/ /* Device API *------------------------------------------------------------------*/ -bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rhport; - (void) rh_init; +bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { + (void)rhport; + (void)rh_init; tu_memclr(&_dcd, sizeof(_dcd)); - USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8); + USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8); USB_OTG_FS->BDT_PAGE_02 = (uint8_t)((uintptr_t)_dcd.bdt >> 16); USB_OTG_FS->BDT_PAGE_03 = (uint8_t)((uintptr_t)_dcd.bdt >> 24); @@ -259,27 +248,24 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; } #define USB_DEVICE_INTERRUPT_PRIORITY (3U) -void dcd_int_enable(uint8_t rhport) -{ +void dcd_int_enable(uint8_t rhport) { uint8_t irqNumber; irqNumber = USB_FS_IRQn; - (void) rhport; - USB_OTG_FS->INT_ENB = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | - USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK; + (void)rhport; + USB_OTG_FS->INT_ENB = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK | + USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK; NVIC_SetPriority((IRQn_Type)irqNumber, USB_DEVICE_INTERRUPT_PRIORITY); NVIC_EnableIRQ(USB_FS_IRQn); } -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; +void dcd_int_disable(uint8_t rhport) { + (void)rhport; NVIC_DisableIRQ(USB_FS_IRQn); USB_OTG_FS->INT_ENB = 0; } -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void) rhport; +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void)rhport; _dcd.addr = dev_addr & 0x7F; /* Response with status first before changing device address */ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); @@ -296,31 +282,29 @@ extern u32 SystemCoreClock; #pragma GCC diagnostic pop #endif -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; +void dcd_remote_wakeup(uint8_t rhport) { + (void)rhport; unsigned cnt = SystemCoreClock / 100; USB_OTG_FS->CTL |= USB_CTL_RESUME_MASK; - while (cnt--) __NOP(); + while (cnt--) { + __NOP(); + } USB_OTG_FS->CTL &= ~USB_CTL_RESUME_MASK; } -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - USB_OTG_FS->CTL |= USB_CTL_USBENSOFEN_MASK; +void dcd_connect(uint8_t rhport) { + (void)rhport; + USB_OTG_FS->CTL |= USB_CTL_USBENSOFEN_MASK; } -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - USB_OTG_FS->CTL = 0; +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + USB_OTG_FS->CTL = 0; } -void dcd_sof_enable(uint8_t rhport, bool en) -{ - (void) rhport; - (void) en; +void dcd_sof_enable(uint8_t rhport, bool en) { + (void)rhport; + (void)en; // TODO implement later } @@ -328,24 +312,23 @@ void dcd_sof_enable(uint8_t rhport, bool en) //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) -{ - (void) rhport; - - const unsigned ep_addr = ep_desc->bEndpointAddress; - const unsigned epn = ep_addr & 0xFu; - const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; - const unsigned xfer = ep_desc->bmAttributes.xfer; - endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; - const unsigned odd = ep->odd; - buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0]; +bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) { + (void)rhport; + + const unsigned ep_addr = ep_desc->bEndpointAddress; + const unsigned epn = ep_addr & 0xFu; + const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; + const unsigned xfer = ep_desc->bmAttributes.xfer; + endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; + const unsigned odd = ep->odd; + buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0]; /* No support for control transfer */ TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL)); ep->max_packet_size = tu_edpt_packet_size(ep_desc); - unsigned val = USB_ENDPT_EPCTLDIS_MASK; - val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0; + unsigned val = USB_ENDPT_EPCTLDIS_MASK; + val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK : 0; val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK; USB_OTG_FS->EP_CTL[epn] |= val; @@ -359,21 +342,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) return true; } -void dcd_edpt_close_all (uint8_t rhport) -{ - (void) rhport; +void dcd_edpt_close_all(uint8_t rhport) { + (void)rhport; // TODO implement dcd_edpt_close_all() } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; - const unsigned epn = ep_addr & 0xFu; - const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; - endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; - buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0]; - const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK; + const unsigned epn = ep_addr & 0xFu; + const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; + endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; + buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0]; + const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK; USB_OTG_FS->EP_CTL[epn] &= ~msk; ep->max_packet_size = 0; ep->length = 0; @@ -381,14 +362,28 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) bd->head = 0; } -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) -{ - (void) rhport; + #if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} + #endif + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { + (void)rhport; NVIC_DisableIRQ(USB_FS_IRQn); - const unsigned epn = ep_addr & 0xFu; - const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; - endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; - buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd]; + const unsigned epn = ep_addr & 0xFu; + const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; + endpoint_state_t *ep = &_dcd.endpoint[epn][dir]; + buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd]; if (bd->own) { TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head); @@ -399,42 +394,40 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to const unsigned mps = ep->max_packet_size; if (total_bytes > mps) { - buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1; + buffer_descriptor_t *next = ep->odd ? bd - 1 : bd + 1; /* When total_bytes is greater than the max packet size, * it prepares to the next transfer to avoid NAK in advance. */ - next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps; + next->bc = total_bytes >= 2 * mps ? mps : total_bytes - mps; next->addr = buffer + mps; next->own = 1; } - bd->bc = total_bytes >= mps ? mps: total_bytes; - bd->addr = buffer; + bd->bc = total_bytes >= mps ? mps : total_bytes; + bd->addr = buffer; __DSB(); - bd->own = 1; /* the own bit must set after addr */ + bd->own = 1; /* the own bit must set after addr */ NVIC_EnableIRQ(USB_FS_IRQn); return true; } -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; const unsigned epn = ep_addr & 0xFu; if (0 == epn) { - USB_OTG_FS->EP_CTL[epn] |= USB_ENDPT_EPSTALL_MASK; + USB_OTG_FS->EP_CTL[epn] |= USB_ENDPT_EPSTALL_MASK; } else { - const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; - buffer_descriptor_t *bd = _dcd.bdt[epn][dir]; - bd[0].bdt_stall = 1; - bd[1].bdt_stall = 1; + const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; + buffer_descriptor_t *bd = _dcd.bdt[epn][dir]; + bd[0].bdt_stall = 1; + bd[1].bdt_stall = 1; } } -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - const unsigned epn = ep_addr & 0xFu; - const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; - const unsigned odd = _dcd.endpoint[epn][dir].odd; - buffer_descriptor_t *bd = _dcd.bdt[epn][dir]; +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void)rhport; + const unsigned epn = ep_addr & 0xFu; + const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT; + const unsigned odd = _dcd.endpoint[epn][dir].odd; + buffer_descriptor_t *bd = _dcd.bdt[epn][dir]; bd[odd ^ 1].own = 0; bd[odd ^ 1].data = 1; @@ -447,19 +440,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) //--------------------------------------------------------------------+ // ISR //--------------------------------------------------------------------+ -void dcd_int_handler(uint8_t rhport) -{ - (void) rhport; +void dcd_int_handler(uint8_t rhport) { + (void)rhport; - uint32_t is = USB_OTG_FS->INT_STAT; - uint32_t msk = USB_OTG_FS->INT_ENB; + uint32_t is = USB_OTG_FS->INT_STAT; + uint32_t msk = USB_OTG_FS->INT_ENB; USB_OTG_FS->INT_STAT = is & ~msk; is &= msk; if (is & USB_ISTAT_ERROR_MASK) { /* TODO: */ - uint32_t es = USB_OTG_FS->ERR_STAT; + uint32_t es = USB_OTG_FS->ERR_STAT; USB_OTG_FS->ERR_STAT = es; - USB_OTG_FS->INT_STAT = is; /* discard any pending events */ + USB_OTG_FS->INT_STAT = is; /* discard any pending events */ return; } @@ -493,5 +485,4 @@ void dcd_int_handler(uint8_t rhport) return; } } - #endif diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 9e5f5117f..730ca7fb1 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -426,6 +426,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { __DSB(); } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} +#endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { (void) rhport; @@ -1062,5 +1077,4 @@ void tusb_hal_nrf_power_event(uint32_t event) { break; } } - #endif diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c index 3d5e195a9..da1c5c888 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -428,6 +428,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) if (ie) NVIC_EnableIRQ(USB0_IRQn); } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + (void) ep_addr; + (void) largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; +} + #endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { (void) rhport; @@ -577,5 +592,4 @@ void dcd_int_handler(uint8_t rhport) process_tokdne(rhport); } } - #endif diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c index 855c59cd1..5516e1ba6 100644 --- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c @@ -337,9 +337,17 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) return true; } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; (void) ep_addr; - // TODO implement dcd_edpt_close() +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } void dcd_edpt_close_all (uint8_t rhport) @@ -600,5 +608,4 @@ void dcd_int_handler(uint8_t rhport) TU_BREAKPOINT(); } } - #endif diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c index 7c637ce0c..1c135f3be 100644 --- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c +++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c @@ -432,6 +432,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1; } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} +#endif + static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) { uint16_t nbytes; ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id); @@ -631,5 +646,4 @@ void dcd_int_handler(uint8_t rhport) // Endpoint transfer complete interrupt process_xfer_isr(rhport, int_status); } - #endif diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c index 60afbd435..25ef117c2 100644 --- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c +++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c @@ -113,6 +113,19 @@ void dcd_edpt_close_all (uint8_t rhport) (void) rhport; } +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} + // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { @@ -207,5 +220,4 @@ void __no_inline_not_in_flash_func(pio_usb_device_irq_handler)(uint8_t root_id) // clear all rport->ints &= ~ints; } - #endif diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index ecd28973c..2cc79aa74 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -859,6 +859,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) _dcd.ep[dir][epn] = 0; } +#if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} +#endif + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { rusb2_reg_t* rusb = RUSB2_REG(rhport); @@ -1028,5 +1043,4 @@ void dcd_int_handler(uint8_t rhport) } } } - #endif diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index f1f4897cb..8e6dc8c63 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -1082,6 +1082,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) musb_int_unmask(); } + #if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} + #endif + // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { @@ -1210,5 +1225,4 @@ void dcd_int_handler(uint8_t rhport) rxis &= ~TU_BIT(num); } } - #endif diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c index 64cbc5087..e4c28a56f 100644 --- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c +++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c @@ -333,11 +333,20 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) return true; } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; (void) ep_addr; - // TODO implement dcd_edpt_close() +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; } +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; +} + + void dcd_edpt_close_all (uint8_t rhport) { (void) rhport; @@ -821,5 +830,4 @@ void dcd_int_handler(uint8_t rhport) } } - #endif diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c index a03c94558..f0e52ae47 100644 --- a/src/portable/valentyusb/eptri/dcd_eptri.c +++ b/src/portable/valentyusb/eptri/dcd_eptri.c @@ -438,9 +438,17 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) return true; } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; (void) ep_addr; - // TODO implement dcd_edpt_close() +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void)rhport; + (void)ep_addr; + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } void dcd_edpt_close_all (uint8_t rhport) @@ -659,5 +667,4 @@ void dcd_int_handler(uint8_t rhport) } } } - #endif diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c index c248ba14e..f8c3b05c3 100644 --- a/src/portable/wch/dcd_ch32_usbfs.c +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -283,12 +283,20 @@ void dcd_edpt_close_all(uint8_t rhport) { // TODO optional } -void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void) rhport; (void) ep_addr; - // TODO optional + (void)largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { + (void)rhport; + (void)desc_ep; + return false; } + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { (void) rhport; uint8_t ep = tu_edpt_number(ep_addr); @@ -344,5 +352,4 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { } } } - #endif diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index 4a208b9df..36fdc89ef 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -27,13 +27,14 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && defined(CFG_TUD_WCH_USBIP_USBHS) && CFG_TUD_WCH_USBIP_USBHS -#include "ch32_usbhs_reg.h" +#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && defined(CFG_TUD_WCH_USBIP_USBHS) && \ + (CFG_TUD_WCH_USBIP_USBHS == 1) + #include "ch32_usbhs_reg.h" -#include "device/dcd.h" + #include "device/dcd.h" -// Max number of bi-directional endpoints including EP0 -#define EP_MAX 16 + // Max number of bi-directional endpoints including EP0 + #define EP_MAX 16 typedef struct { uint8_t* buffer; @@ -288,6 +289,21 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { } } + #if 0 +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + (void) ep_addr; + (void) largest_packet_size; + return false; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { + (void) rhport; + (void) desc_ep; + return false; +} + #endif + void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; @@ -419,5 +435,4 @@ void dcd_int_handler(uint8_t rhport) { USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */ } } - #endif -- cgit v1.3.1 From a124edf9f6d008074f838c6daaed7c0502569ab7 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 7 Jan 2026 09:58:23 +0700 Subject: refactor remove endpoint_control/buffer_control from hw_endpoint_t --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 49 ++++++++------- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 33 +++++------ src/portable/raspberrypi/rp2040/rp2040_usb.c | 29 ++++----- src/portable/raspberrypi/rp2040/rp2040_usb.h | 89 +++++++++++++++++++++------- 4 files changed, 123 insertions(+), 77 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 15852f29f..31683502a 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -55,14 +55,14 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; // SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd static bool _sof_enable = false; -TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) { +TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get(uint8_t num, tusb_dir_t dir) { return &hw_endpoints[num][dir]; } TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) { uint8_t num = tu_edpt_number(ep_addr); tusb_dir_t dir = tu_edpt_dir(ep_addr); - return hw_endpoint_get_by_num(num, dir); + return hw_endpoint_get(num, dir); } // Allocate from the USB buffer space (max 3840 bytes) @@ -86,15 +86,14 @@ static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) { // Enable endpoint TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) { uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); - *ep->endpoint_control = reg; + *hwep_ctrl_reg(ep) = reg; } // main processing for dcd_edpt_iso_activate static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - const uint8_t num = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); + hw_endpoint_t* ep = hw_endpoint_get(num, dir); ep->ep_addr = ep_addr; @@ -106,28 +105,28 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t ep->transfer_type = transfer_type; // Every endpoint has a buffer control register in dpram - if (dir == TUSB_DIR_IN) { - ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in; - } else { - ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out; - } + // if (dir == TUSB_DIR_IN) { + // ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in; + // } else { + // ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out; + // } // Clear existing buffer control state - *ep->buffer_control = 0; + *hwep_buf_ctrl_reg(ep) = 0; if (num == 0) { // EP0 has no endpoint control register because the buffer offsets are fixed - ep->endpoint_control = NULL; + // ep->endpoint_control = NULL; // Buffer offset is fixed (also double buffered) ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; } else { // Set the endpoint control register (starts at EP1, hence num-1) - if (dir == TUSB_DIR_IN) { - ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in; - } else { - ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; - } + // if (dir == TUSB_DIR_IN) { + // ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in; + // } else { + // ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; + // } } } @@ -165,7 +164,7 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { buf_ctrl |= USB_BUF_CTRL_DATA1_PID; } - _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl); + hwep_buf_ctrl_set(ep, buf_ctrl); hw_endpoint_reset_transfer(ep); if (rp2040_chip_version() >= 2) { @@ -184,7 +183,7 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) { usb_hw_clear->buf_status = bit; // IN transfer for even i, OUT transfer for odd i - struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); + struct hw_endpoint *ep = hw_endpoint_get(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); // Continue xfer bool done = hw_endpoint_xfer_continue(ep); @@ -204,7 +203,7 @@ TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) { // If we have finished this transfer on EP0 set pid back to 1 for next // setup transfer. Also clear a stall in case for (uint8_t dir = 0; dir < 2; dir++) { - struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir); + struct hw_endpoint *ep = hw_endpoint_get(0, dir); ep->next_pid = 1u; if (ep->active) { hw_endpoint_abort_xfer(ep); // Abort any pending transfer per USB specs @@ -240,7 +239,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { e15_last_sof = time_us_32(); for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) { - struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN); + struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) { @@ -369,9 +368,9 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version()); // Reset hardware to default state - rp2040_usb_init(); + rp2usb_init(); -#if FORCE_VBUS_DETECT + #if FORCE_VBUS_DETECT // Force VBUS detect so the device thinks it is plugged into a host usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; #endif @@ -546,7 +545,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { // stall and clear current pending buffer // may need to use EP_ABORT - _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL); + hwep_buf_ctrl_set(ep, USB_BUF_CTRL_STALL); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -557,7 +556,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // clear stall also reset toggle to DATA0, ready for next transfer ep->next_pid = 0; - _hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL); + hwep_buf_ctrl_clear_mask(ep, USB_BUF_CTRL_STALL); } } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index cd8c905d5..12a2f2d25 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -122,7 +122,7 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) remaining_buffers &= ~bit; struct hw_endpoint * ep = &epx; - uint32_t ep_ctrl = *ep->endpoint_control; + uint32_t ep_ctrl = *hwep_ctrl_reg(ep); if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS ) { TU_LOG(3, "Double Buffered: "); @@ -240,9 +240,8 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) if ( status & USB_INTS_ERROR_DATA_SEQ_BITS ) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; - TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", - tu_u32_low16(*epx.buffer_control), - tu_u32_high16(*epx.buffer_control)); + TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hw_endpoint_get_buf_ctrl(&epx)), + tu_u32_high16(*hw_endpoint_get_buf_ctrl(&epx))); panic("Data Seq Error \n"); } @@ -285,8 +284,8 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) ep = _next_free_interrupt_ep(); pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num); assert(ep); - ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; - ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; + // ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; + // ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; // 0 for epx (double buffered): TODO increase to 1024 for ISO // 2x64 for intep0 // 3x64 for intep1 @@ -296,8 +295,8 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) else { ep = &epx; - ep->buffer_control = &usbh_dpram->epx_buf_ctrl; - ep->endpoint_control = &usbh_dpram->epx_ctrl; + // ep->buffer_control = &usbh_dpram->epx_buf_ctrl; + // ep->endpoint_control = &usbh_dpram->epx_ctrl; ep->hw_data_buf = &usbh_dpram->epx_data[0]; } @@ -307,8 +306,8 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval) { // Already has data buffer, endpoint control, and buffer control allocated at this point - assert(ep->endpoint_control); - assert(ep->buffer_control); + // assert(ep->endpoint_control); + // assert(ep->buffer_control); assert(ep->hw_data_buf); uint8_t const num = tu_edpt_number(ep_addr); @@ -340,8 +339,8 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t { ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); } - *ep->endpoint_control = ep_reg; - pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg); + *hwep_ctrl_reg(ep) = ep_reg; + // pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg); ep->configured = true; if ( ep != &epx ) @@ -382,7 +381,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { assert(rhport == 0); // Reset any previous state - rp2040_usb_init(); + rp2usb_init(); // Force VBUS detect to always present, for now we assume vbus is always provided (without using VBUS En) usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; @@ -466,8 +465,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // reset epx if it is currently active with unplugged device if (epx.configured && epx.active && epx.dev_addr == dev_addr) { epx.configured = false; - *epx.endpoint_control = 0; - *epx.buffer_control = 0; + *hwep_ctrl_reg(&epx) = 0; + *hwep_buf_ctrl_reg(&epx) = 0; hw_endpoint_reset_transfer(&epx); } @@ -482,8 +481,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // unconfigure the endpoint ep->configured = false; - *ep->endpoint_control = 0; - *ep->buffer_control = 0; + *hwep_ctrl_reg(ep) = 0; + *hwep_buf_ctrl_reg(ep) = 0; hw_endpoint_reset_transfer(ep); } } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 9d0bd762d..61f978b0d 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -62,7 +62,7 @@ static void unaligned_memcpy(void *dst, const void *src, size_t n) { } } -void rp2040_usb_init(void) { +void rp2usb_init(void) { // Reset usb controller reset_block(RESETS_RESET_USBCTRL_BITS); unreset_block_wait(RESETS_RESET_USBCTRL_BITS); @@ -93,21 +93,21 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->user_buf = 0; } -void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask, - uint32_t or_mask) { +void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) { uint32_t value = 0; + io_rw_32 *buf_ctrl = hwep_buf_ctrl_reg(ep); if (and_mask) { - value = *ep->buffer_control & and_mask; + value = *buf_ctrl & and_mask; } if (or_mask) { value |= or_mask; if (or_mask & USB_BUF_CTRL_AVAIL) { - if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) { + if (*buf_ctrl & USB_BUF_CTRL_AVAIL) { panic("ep %02X was already available", ep->ep_addr); } - *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL; + *buf_ctrl = value & ~USB_BUF_CTRL_AVAIL; // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control // Don't need delay in host mode as host is in charge if (!is_host_mode()) { @@ -116,7 +116,7 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi } } - *ep->buffer_control = value; + *buf_ctrl = value; } // prepare buffer, return buffer control @@ -153,7 +153,8 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, // Prepare buffer control register value void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { - uint32_t ep_ctrl = *ep->endpoint_control; + io_rw_32 *ep_ctrl_reg = hwep_ctrl_reg(ep); + uint32_t ep_ctrl = *ep_ctrl_reg; // always compute and start with buffer 0 uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL; @@ -182,13 +183,13 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; } - *ep->endpoint_control = ep_ctrl; + *ep_ctrl_reg = ep_ctrl; TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); // Finally, write to buffer_control which will trigger the transfer // the next time the controller polls this dpram address - _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl); + hwep_buf_ctrl_set(ep, buf_ctrl); } void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { @@ -221,7 +222,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to // sync endpoint buffer and return transferred bytes static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { - uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); + uint32_t buf_ctrl = hwep_buf_ctrl_get(ep); if (buf_id) buf_ctrl = buf_ctrl >> 16; uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; @@ -256,14 +257,14 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) // Update hw endpoint struct with info from hardware // after a buff status interrupt - uint32_t __unused buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); + uint32_t __unused buf_ctrl = hwep_buf_ctrl_get(ep); TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); // always sync buffer 0 uint16_t buf0_bytes = sync_ep_buffer(ep, 0); // sync buffer 1 if double buffered - if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) { + if ((*hwep_ctrl_reg(ep)) & EP_CTRL_DOUBLE_BUFFERED_BITS) { if (buf0_bytes == ep->wMaxPacketSize) { // sync buffer 1 if not short packet sync_ep_buffer(ep, 1); @@ -287,7 +288,7 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; - _hw_endpoint_buffer_control_set_value32(ep, 0); + hwep_buf_ctrl_set(ep, 0); usb_hw->abort &= ~TU_BIT(ep_id); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index d4d29a816..603640c6d 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -56,10 +56,10 @@ typedef struct hw_endpoint uint8_t next_pid; // Endpoint control register - io_rw_32 *endpoint_control; + // io_rw_32 *endpoint_control; // Buffer control register - io_rw_32 *buffer_control; + // io_rw_32 *buffer_control; // Buffer pointer in usb dpram uint8_t *hw_data_buf; @@ -97,7 +97,12 @@ typedef struct hw_endpoint extern volatile uint32_t e15_last_sof; #endif -void rp2040_usb_init(void); +void rp2usb_init(void); + +// if usb hardware is in host mode +TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { + return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; +} void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len); bool hw_endpoint_xfer_continue(struct hw_endpoint *ep); @@ -110,34 +115,76 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. } -void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg(struct hw_endpoint *ep) { + (void)ep; +#if CFG_TUH_ENABLED + if (rp2usb_is_host_mode()) { + if (ep->transfer_type == TUSB_XFER_CONTROL) { + return &usbh_dpram->epx_ctrl; + } + return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; + } +#endif -TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep) -{ - return *ep->buffer_control; +#if CFG_TUD_ENABLED + if (!rp2usb_is_host_mode()) { + const uint8_t num = tu_edpt_number(ep->ep_addr); + if (num == 0) { + return NULL; + } + return (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[num - 1].in + : &usb_dpram->ep_ctrl[num - 1].out; + } +#endif + + return NULL; } -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value) -{ - _hw_endpoint_buffer_control_update32(ep, 0, value); +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg(struct hw_endpoint *ep) { + (void)ep; +#if CFG_TUH_ENABLED + if (rp2usb_is_host_mode()) { + if (ep->transfer_type == TUSB_XFER_CONTROL) { + return &usbh_dpram->epx_buf_ctrl; + } + return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; + } +#endif + +#if CFG_TUD_ENABLED + if (!rp2usb_is_host_mode()) { + const uint8_t num = tu_edpt_number(ep->ep_addr); + return (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[num].in + : &usb_dpram->ep_buf_ctrl[num].out; + } +#endif + return NULL; } -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value) -{ - _hw_endpoint_buffer_control_update32(ep, ~value, value); +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +void hwep_buf_ctrl_update(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); + +TU_ATTR_ALWAYS_INLINE static inline uint32_t hwep_buf_ctrl_get(struct hw_endpoint *ep) { + return *hwep_buf_ctrl_reg(ep); } -TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value) -{ - _hw_endpoint_buffer_control_update32(ep, ~value, 0); +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set(struct hw_endpoint *ep, uint32_t value) { + hwep_buf_ctrl_update(ep, 0, value); } -static inline uintptr_t hw_data_offset (uint8_t *buf) -{ - // Remove usb base from buffer pointer - return (uintptr_t) buf ^ (uintptr_t) usb_dpram; +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set_mask32(struct hw_endpoint *ep, uint32_t value) { + hwep_buf_ctrl_update(ep, ~value, value); +} + +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_clear_mask(struct hw_endpoint *ep, uint32_t value) { + hwep_buf_ctrl_update(ep, ~value, 0); } -extern const char *ep_dir_string[]; +static inline uintptr_t hw_data_offset(uint8_t *buf) { + // Remove usb base from buffer pointer + return (uintptr_t)buf ^ (uintptr_t)usb_dpram; +} #endif -- cgit v1.3.1 From 475e97cc8b048e2ea0244f310657b8e02a5cfe73 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 7 Jan 2026 22:49:18 +0700 Subject: remove hw_endpoint_t rx, rename/change signature of helper functions --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 205 ++++++++++++--------------- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 15 +- src/portable/raspberrypi/rp2040/rp2040_usb.c | 182 +++++++++++++++--------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 69 ++++----- 4 files changed, 239 insertions(+), 232 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 31683502a..6c74029d8 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -41,83 +41,49 @@ // Current implementation force vbus detection as always present, causing device think it is always plugged into host. // Therefore it cannot detect disconnect event, mistaken it as suspend. // Note: won't work if change to 0 (for now) -#define FORCE_VBUS_DETECT 1 +#define FORCE_VBUS_DETECT 1 + + #define USB_INTS_ERROR_BITS \ + (USB_INTS_ERROR_DATA_SEQ_BITS | USB_INTS_ERROR_BIT_STUFF_BITS | USB_INTS_ERROR_CRC_BITS | \ + USB_INTS_ERROR_RX_OVERFLOW_BITS | USB_INTS_ERROR_RX_TIMEOUT_BITS) /*------------------------------------------------------------------*/ /* Low level controller *------------------------------------------------------------------*/ -// Init these in dcd_init -static uint8_t* next_buffer_ptr; +// HW buffer pointer from USB buffer space (max 3840 bytes) +static uint8_t *hw_buffer_ptr; // USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in. static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; -// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd +// SOF may be used by remote wakeup as RESUME, this indicates whether SOF is actually used by usbd static bool _sof_enable = false; -TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get(uint8_t num, tusb_dir_t dir) { - return &hw_endpoints[num][dir]; +TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get(uint8_t epnum, tusb_dir_t dir) { + return &hw_endpoints[epnum][dir]; } -TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) { - uint8_t num = tu_edpt_number(ep_addr); - tusb_dir_t dir = tu_edpt_dir(ep_addr); +TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8_t ep_addr) { + const uint8_t num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); return hw_endpoint_get(num, dir); } -// Allocate from the USB buffer space (max 3840 bytes) -static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) { - // round up size to multiple of 64 - size = tu_round_up(ep->wMaxPacketSize, 64); - - // double buffered Bulk endpoint - if (ep->transfer_type == TUSB_XFER_BULK) { - size *= 2u; - } - - // assign buffer - ep->hw_data_buf = next_buffer_ptr; - next_buffer_ptr += size; - - hard_assert(next_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data)); - pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); -} - -// Enable endpoint -TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) { - uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); - *hwep_ctrl_reg(ep) = reg; -} - // main processing for dcd_edpt_iso_activate -static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - const uint8_t num = tu_edpt_number(ep_addr); - const tusb_dir_t dir = tu_edpt_dir(ep_addr); - hw_endpoint_t* ep = hw_endpoint_get(num, dir); - - ep->ep_addr = ep_addr; - - // For device, IN is a tx transfer and OUT is an rx transfer - ep->rx = (dir == TUSB_DIR_OUT); +static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { + const uint8_t epnum = tu_edpt_number(ep_addr); - ep->next_pid = 0u; + ep->ep_addr = ep_addr; + ep->next_pid = 0u; ep->wMaxPacketSize = wMaxPacketSize; - ep->transfer_type = transfer_type; - - // Every endpoint has a buffer control register in dpram - // if (dir == TUSB_DIR_IN) { - // ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in; - // } else { - // ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out; - // } + ep->transfer_type = transfer_type; // Clear existing buffer control state - *hwep_buf_ctrl_reg(ep) = 0; - - if (num == 0) { - // EP0 has no endpoint control register because the buffer offsets are fixed - // ep->endpoint_control = NULL; + io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + *buf_ctrl_reg = 0; + // allocated hw buffer + if (epnum == 0) { // Buffer offset is fixed (also double buffered) ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; } else { @@ -127,33 +93,49 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t // } else { // ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; // } + + // round up size to multiple of 64 + uint16_t size = (uint16_t)tu_round_up(wMaxPacketSize, 64); + + // double buffered Bulk endpoint + if (transfer_type == TUSB_XFER_BULK) { + size *= 2u; + } + + // assign buffer + ep->hw_data_buf = hw_buffer_ptr; + hw_buffer_ptr += size; + + hard_assert(hw_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data)); + pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); } } // Init, allocate buffer and enable endpoint static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_init(ep_addr, wMaxPacketSize, transfer_type); - const uint8_t num = tu_edpt_number(ep_addr); - if (num != 0) { - // EP0 is already enabled - hw_endpoint_alloc(ep, ep->wMaxPacketSize); - hw_endpoint_enable(ep); + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + + hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type); + + // Set endpoint control register to enable (EP0 has no endpoint control register) + io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep); + if (ctrl_reg != NULL) { + const uint32_t ctrl_value = + EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); + *ctrl_reg = ctrl_value; } } -static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_xfer_start(ep, buffer, total_bytes); -} - static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { // Abort any pending transfer // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1). // Which means we are not guaranteed to safely abort pending transfer on B0 and B1. - const uint8_t dir = tu_edpt_dir(ep->ep_addr); + const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); const uint8_t epnum = tu_edpt_number(ep->ep_addr); const uint32_t abort_mask = TU_BIT((epnum << 1) | (dir ? 0 : 1)); + if (rp2040_chip_version() >= 2) { usb_hw_set->abort = abort_mask; while ((usb_hw->abort_done & abort_mask) != abort_mask) {} @@ -173,7 +155,7 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { } } -static void __tusb_irq_path_func(hw_handle_buff_status)(void) { +static void __tusb_irq_path_func(handle_hw_buff_status)(void) { uint32_t remaining_buffers = usb_hw->buf_status; pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); uint bit = 1u; @@ -183,12 +165,13 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) { usb_hw_clear->buf_status = bit; // IN transfer for even i, OUT transfer for odd i - struct hw_endpoint *ep = hw_endpoint_get(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); + const uint8_t epnum = i >> 1u; + const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - // Continue xfer - bool done = hw_endpoint_xfer_continue(ep); + const bool done = hw_endpoint_xfer_continue(ep); if (done) { - // Notify + // Notify usbd const uint16_t xferred_len = ep->xferred_len; hw_endpoint_reset_transfer(ep); dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); @@ -222,11 +205,11 @@ static void __tusb_irq_path_func(reset_non_control_endpoints)(void) { tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t)); // reclaim buffer space - next_buffer_ptr = &usb_dpram->epx_data[0]; + hw_buffer_ptr = &usb_dpram->epx_data[0]; } static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { - uint32_t const status = usb_hw->ints; + const uint32_t status = usb_hw->ints; uint32_t handled = 0; if (status & USB_INTF_DEV_SOF_BITS) { @@ -259,7 +242,9 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { #endif // disable SOF interrupt if it is used for RESUME in remote wakeup - if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; + if (!keep_sof_alive && !_sof_enable) { + usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; + } dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true); } @@ -268,7 +253,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { // before closing the EP, the events will be delivered in same order. if (status & USB_INTS_BUFF_STATUS_BITS) { handled |= USB_INTS_BUFF_STATUS_BITS; - hw_handle_buff_status(); + handle_hw_buff_status(); } if (status & USB_INTS_SETUP_REQ_BITS) { @@ -345,13 +330,6 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { } } -#define USB_INTS_ERROR_BITS ( \ - USB_INTS_ERROR_DATA_SEQ_BITS | \ - USB_INTS_ERROR_BIT_STUFF_BITS | \ - USB_INTS_ERROR_CRC_BITS | \ - USB_INTS_ERROR_RX_OVERFLOW_BITS | \ - USB_INTS_ERROR_RX_TIMEOUT_BITS) - /*------------------------------------------------------------------*/ /* Controller API *------------------------------------------------------------------*/ @@ -424,12 +402,11 @@ void dcd_int_disable(__unused uint8_t rhport) { irq_set_enabled(USBCTRL_IRQ, false); } -void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) { - assert(rhport == 0); - +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void)dev_addr; // Can't set device address in hardware until status xfer has complete // Send 0len complete response on EP0 IN - hw_endpoint_xfer(0x80, NULL, 0); + dcd_edpt_xfer(rhport, 0x80, NULL, 0, false); } void dcd_remote_wakeup(__unused uint8_t rhport) { @@ -474,7 +451,6 @@ void dcd_sof_enable(uint8_t rhport, bool en) { /*------------------------------------------------------------------*/ /* DCD Endpoint port *------------------------------------------------------------------*/ - void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { (void) rhport; @@ -496,25 +472,32 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { // New API: Allocate packet buffer used by ISO endpoints // Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { - (void) rhport; - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_init(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS); - hw_endpoint_alloc(ep, largest_packet_size); + (void)rhport; + struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_init(ep, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS); return true; } // New API: Configure and enable an ISO endpoint according to descriptor -bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { - (void) rhport; - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_desc->bEndpointAddress); +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) { + (void)rhport; + const uint8_t epnum = tu_edpt_number(ep_desc->bEndpointAddress); + const tusb_dir_t dir = tu_edpt_dir(ep_desc->bEndpointAddress); + struct hw_endpoint *ep = hw_endpoint_get(epnum, dir); TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and allocated previously if (ep->active) { hw_endpoint_abort_xfer(ep); // abort any pending transfer } - ep->wMaxPacketSize = ep_desc->wMaxPacketSize; - hw_endpoint_enable(ep); + + // Set control register to enable endpoint + io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep); + if (ctrl_reg != NULL) { + const uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | ((uint32_t)TUSB_XFER_ISOCHRONOUS << EP_CTRL_BUFFER_TYPE_LSB) | + hw_data_offset(ep->hw_data_buf); + *ctrl_reg = ctrl_value; + } return true; } @@ -525,26 +508,26 @@ void dcd_edpt_close_all(uint8_t rhport) { reset_non_control_endpoints(); } -bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) { - (void) is_isr; - assert(rhport == 0); - hw_endpoint_xfer(ep_addr, buffer, total_bytes); +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { + (void)rhport; + (void)is_isr; + hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_xfer_start(ep, buffer, total_bytes); return true; } void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { - (void) rhport; + (void)rhport; + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - if (tu_edpt_number(ep_addr) == 0) { + if (epnum == 0) { // A stall on EP0 has to be armed so it can be cleared on the next setup packet - usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS - : USB_EP_STALL_ARM_EP0_OUT_BITS; + usb_hw_set->ep_stall_arm = (dir == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS; } - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - - // stall and clear current pending buffer - // may need to use EP_ABORT + // stall and clear current pending buffer, may need to use EP_ABORT hwep_buf_ctrl_set(ep, USB_BUF_CTRL_STALL); } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 12a2f2d25..5ec84be16 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -122,7 +122,7 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) remaining_buffers &= ~bit; struct hw_endpoint * ep = &epx; - uint32_t ep_ctrl = *hwep_ctrl_reg(ep); + uint32_t ep_ctrl = *hwep_ctrl_reg_host(ep); if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS ) { TU_LOG(3, "Double Buffered: "); @@ -316,9 +316,6 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep->ep_addr = ep_addr; ep->dev_addr = dev_addr; - // For host, IN to host == RX, anything else rx == false - ep->rx = (dir == TUSB_DIR_IN); - // Response to a setup packet on EP0 starts with pid of 1 ep->next_pid = (num == 0 ? 1u : 0u); ep->wMaxPacketSize = wMaxPacketSize; @@ -339,7 +336,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t { ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); } - *hwep_ctrl_reg(ep) = ep_reg; + *hwep_ctrl_reg_host(ep) = ep_reg; // pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg); ep->configured = true; @@ -465,8 +462,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // reset epx if it is currently active with unplugged device if (epx.configured && epx.active && epx.dev_addr == dev_addr) { epx.configured = false; - *hwep_ctrl_reg(&epx) = 0; - *hwep_buf_ctrl_reg(&epx) = 0; + *hwep_ctrl_reg_host(&epx) = 0; + *hwep_buf_ctrl_reg_host(&epx) = 0; hw_endpoint_reset_transfer(&epx); } @@ -481,8 +478,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // unconfigure the endpoint ep->configured = false; - *hwep_ctrl_reg(ep) = 0; - *hwep_buf_ctrl_reg(ep) = 0; + *hwep_ctrl_reg_host(ep) = 0; + *hwep_buf_ctrl_reg_host(ep) = 0; hw_endpoint_reset_transfer(ep); } } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 61f978b0d..8e7bf2dae 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -35,20 +35,15 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ -static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep); +static void hwep_xfer_sync(hw_endpoint_t *ep); -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX - static bool e15_is_bulkin_ep(struct hw_endpoint* ep); - static bool e15_is_critical_frame_period(struct hw_endpoint* ep); -#else - #define e15_is_bulkin_ep(x) (false) - #define e15_is_critical_frame_period(x) (false) -#endif - -// if usb hardware is in host mode -TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) { - return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; -} + #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +static bool e15_is_bulkin_ep(struct hw_endpoint *ep); +static bool e15_is_critical_frame_period(struct hw_endpoint *ep); + #else + #define e15_is_bulkin_ep(x) (false) + #define e15_is_critical_frame_period(x) (false) + #endif //--------------------------------------------------------------------+ // Implementation @@ -94,8 +89,18 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { } void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) { + const bool is_host = rp2usb_is_host_mode(); uint32_t value = 0; - io_rw_32 *buf_ctrl = hwep_buf_ctrl_reg(ep); + io_rw_32 *buf_ctrl; + + #if CFG_TUH_ENABLED + if (is_host) { + buf_ctrl = hwep_buf_ctrl_reg_host(ep); + } else + #endif + { + buf_ctrl = hwep_buf_ctrl_reg_device(ep); + } if (and_mask) { value = *buf_ctrl & and_mask; @@ -110,7 +115,7 @@ void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t *buf_ctrl = value & ~USB_BUF_CTRL_AVAIL; // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control // Don't need delay in host mode as host is in charge - if (!is_host_mode()) { + if (!is_host) { busy_wait_at_least_cycles(12); } } @@ -119,9 +124,9 @@ void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t *buf_ctrl = value; } -// prepare buffer, return buffer control -static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { - uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); +// prepare buffer, move data if tx, return buffer control +static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { + const uint16_t buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; @@ -130,7 +135,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; ep->next_pid ^= 1u; - if (!ep->rx) { + if (!is_rx) { // Copy data from user buffer to hw buffer unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); ep->user_buf += buflen; @@ -146,44 +151,66 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, buf_ctrl |= USB_BUF_CTRL_LAST; } - if (buf_id) buf_ctrl = buf_ctrl << 16; + if (buf_id) { + buf_ctrl = buf_ctrl << 16; + } return buf_ctrl; } // Prepare buffer control register value void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { - io_rw_32 *ep_ctrl_reg = hwep_ctrl_reg(ep); - uint32_t ep_ctrl = *ep_ctrl_reg; + const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); + bool is_rx; + + io_rw_32 *ep_ctrl_reg; + // io_rw_32 *buf_ctrl_reg; + + #if CFG_TUH_ENABLED + const bool is_host = rp2usb_is_host_mode(); + if (is_host) { + // buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); + ep_ctrl_reg = hwep_ctrl_reg_host(ep); + is_rx = (dir == TUSB_DIR_IN); + } else + #endif + { + // buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + ep_ctrl_reg = hwep_ctrl_reg_device(ep); + is_rx = (dir == TUSB_DIR_OUT); + } // always compute and start with buffer 0 - uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL; - - // For now: skip double buffered for OUT endpoint in Device mode, since - // host could send < 64 bytes and cause short packet on buffer0 - // NOTE: this could happen to Host mode IN endpoint - // Also, Host mode "interrupt" endpoint hardware is only single buffered, - // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint - bool const is_host = is_host_mode(); - bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) || - (is_host && tu_edpt_number(ep->ep_addr) != 0); - - if (ep->remaining_len && !force_single) { - // Use buffer 1 (double buffered) if there is still data - // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) - - buf_ctrl |= prepare_ep_buffer(ep, 1); - - // Set endpoint control double buffered bit if needed - ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; - ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; - } else { - // Single buffered since 1 is enough - ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); - ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; - } + uint32_t buf_ctrl = prepare_ep_buffer(ep, 0, is_rx) | USB_BUF_CTRL_SEL; + + // EP0 has no endpoint control register, also usbd only schedule 1 packet at a time (single buffer) + if (ep_ctrl_reg != NULL) { + uint32_t ep_ctrl = *ep_ctrl_reg; + + // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause + // short packet on buffer0 + // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single + // buffered, + // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint + const bool force_single = is_rx; - *ep_ctrl_reg = ep_ctrl; + if (ep->remaining_len && !force_single) { + // Use buffer 1 (double buffered) if there is still data + // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) + + buf_ctrl |= prepare_ep_buffer(ep, 1, is_rx); + + // Set endpoint control double buffered bit if needed + ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; + ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; + } else { + // Single buffered since 1 is enough + ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); + ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; + } + + *ep_ctrl_reg = ep_ctrl; + } TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); @@ -221,13 +248,16 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { - uint32_t buf_ctrl = hwep_buf_ctrl_get(ep); - if (buf_id) buf_ctrl = buf_ctrl >> 16; +static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, + bool is_rx) { + uint32_t buf_ctrl = *buf_ctrl_reg; + if (buf_id) { + buf_ctrl = buf_ctrl >> 16; + } - uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; + const uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; - if (!ep->rx) { + if (!is_rx) { // We are continuing a transfer here. If we are TX, we have successfully // sent some data can increase the length we have sent assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); @@ -245,7 +275,6 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin // Short packet if (xferred_bytes < ep->wMaxPacketSize) { - pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes); // Reduce total length as this is last packet ep->remaining_len = 0; } @@ -253,21 +282,38 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin return xferred_bytes; } -static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) { - // Update hw endpoint struct with info from hardware - // after a buff status interrupt - - uint32_t __unused buf_ctrl = hwep_buf_ctrl_get(ep); - TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); +// Update hw endpoint struct with info from hardware after a buff status interrupt +static void __tusb_irq_path_func(hwep_xfer_sync)(hw_endpoint_t *ep) { + // const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); + + io_rw_32 *buf_ctrl_reg; + io_rw_32 *ep_ctrl_reg; + bool is_rx; + + #if CFG_TUH_ENABLED + const bool is_host = rp2usb_is_host_mode(); + if (is_host) { + buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); + ep_ctrl_reg = hwep_ctrl_reg_host(ep); + is_rx = (dir == TUSB_DIR_IN); + } else + #endif + { + buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + ep_ctrl_reg = hwep_ctrl_reg_device(ep); + is_rx = (dir == TUSB_DIR_OUT); + } - // always sync buffer 0 - uint16_t buf0_bytes = sync_ep_buffer(ep, 0); + TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_ctrl_reg), + tu_u32_high16(*buf_ctrl_reg)); + uint16_t buf0_bytes = sync_ep_buffer(ep, buf_ctrl_reg, 0, is_rx); // always sync buffer 0 // sync buffer 1 if double buffered - if ((*hwep_ctrl_reg(ep)) & EP_CTRL_DOUBLE_BUFFERED_BITS) { + if (ep_ctrl_reg != NULL && (*ep_ctrl_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) { if (buf0_bytes == ep->wMaxPacketSize) { // sync buffer 1 if not short packet - sync_ep_buffer(ep, 1); + sync_ep_buffer(ep, buf_ctrl_reg, 1, is_rx); } else { // short packet on buffer 0 // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example @@ -309,7 +355,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { } // Update EP struct from hardware state - _hw_endpoint_xfer_sync(ep); + hwep_xfer_sync(ep); // Now we have synced our state with the hardware. Is there more data to transfer? // If we are done then notify tinyusb @@ -336,6 +382,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { //--------------------------------------------------------------------+ #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +// E15 is fixed with RP2350 /* Don't mark IN buffers as available during the last 200us of a full-speed frame. This avoids a situation seen with the USB2.0 hub on a Raspberry @@ -356,9 +403,8 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { volatile uint32_t e15_last_sof = 0; // check if Errata 15 is needed for this endpoint i.e device bulk-in -static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) { - return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN && - ep->transfer_type == TUSB_XFER_BULK); +static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint *ep) { + return (!rp2usb_is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN && ep->transfer_type == TUSB_XFER_BULK); } // check if we need to apply Errata 15 workaround : i.e diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 603640c6d..2ec00694e 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -48,10 +48,6 @@ typedef struct hw_endpoint // Is this a valid struct bool configured; - // Transfer direction (i.e. IN is rx for host but tx for device) - // allows us to common up transfer functions - bool rx; - uint8_t ep_addr; uint8_t next_pid; @@ -115,61 +111,46 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg(struct hw_endpoint *ep) { - (void)ep; -#if CFG_TUH_ENABLED - if (rp2usb_is_host_mode()) { - if (ep->transfer_type == TUSB_XFER_CONTROL) { - return &usbh_dpram->epx_ctrl; - } - return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; +// #if CFG_TUD_ENABLED +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) { + uint8_t const epnum = tu_edpt_number(ep->ep_addr); + const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); + if (epnum == 0) { + // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled + return NULL; } -#endif - -#if CFG_TUD_ENABLED - if (!rp2usb_is_host_mode()) { - const uint8_t num = tu_edpt_number(ep->ep_addr); - if (num == 0) { - return NULL; - } - return (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[num - 1].in - : &usb_dpram->ep_ctrl[num - 1].out; - } -#endif + return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out; +} - return NULL; +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_device(struct hw_endpoint *ep) { + const uint8_t epnum = tu_edpt_number(ep->ep_addr); + const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); + return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out; } +// #endif -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg(struct hw_endpoint *ep) { - (void)ep; #if CFG_TUH_ENABLED - if (rp2usb_is_host_mode()) { - if (ep->transfer_type == TUSB_XFER_CONTROL) { - return &usbh_dpram->epx_buf_ctrl; - } - return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(struct hw_endpoint *ep) { + if (ep->transfer_type == TUSB_XFER_CONTROL) { + return &usbh_dpram->epx_ctrl; } -#endif + return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; +} -#if CFG_TUD_ENABLED - if (!rp2usb_is_host_mode()) { - const uint8_t num = tu_edpt_number(ep->ep_addr); - return (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[num].in - : &usb_dpram->ep_buf_ctrl[num].out; +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_host(struct hw_endpoint *ep) { + if (ep->transfer_type == TUSB_XFER_CONTROL) { + return &usbh_dpram->epx_buf_ctrl; } -#endif - return NULL; + return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; } +#endif + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ void hwep_buf_ctrl_update(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); -TU_ATTR_ALWAYS_INLINE static inline uint32_t hwep_buf_ctrl_get(struct hw_endpoint *ep) { - return *hwep_buf_ctrl_reg(ep); -} - TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set(struct hw_endpoint *ep, uint32_t value) { hwep_buf_ctrl_update(ep, 0, value); } -- cgit v1.3.1 From f0a3d448347047c9df62518fbd641502b2d55333 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 8 Jan 2026 00:54:21 +0700 Subject: change signature for hwep_buf_ctrl_* to take pointer to buf control instead of hwep --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 16 ++++----- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 6 ---- src/portable/raspberrypi/rp2040/rp2040_usb.c | 42 ++++++++++------------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 50 ++++++++++------------------ 4 files changed, 42 insertions(+), 72 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 6c74029d8..5c21590c4 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -87,13 +87,6 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa // Buffer offset is fixed (also double buffered) ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; } else { - // Set the endpoint control register (starts at EP1, hence num-1) - // if (dir == TUSB_DIR_IN) { - // ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in; - // } else { - // ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; - // } - // round up size to multiple of 64 uint16_t size = (uint16_t)tu_round_up(wMaxPacketSize, 64); @@ -146,7 +139,8 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { buf_ctrl |= USB_BUF_CTRL_DATA1_PID; } - hwep_buf_ctrl_set(ep, buf_ctrl); + io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + hwep_buf_ctrl_set(buf_ctrl_reg, buf_ctrl); hw_endpoint_reset_transfer(ep); if (rp2040_chip_version() >= 2) { @@ -528,7 +522,8 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { } // stall and clear current pending buffer, may need to use EP_ABORT - hwep_buf_ctrl_set(ep, USB_BUF_CTRL_STALL); + io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + hwep_buf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -539,7 +534,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // clear stall also reset toggle to DATA0, ready for next transfer ep->next_pid = 0; - hwep_buf_ctrl_clear_mask(ep, USB_BUF_CTRL_STALL); + io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + hwep_buf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL); } } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 5ec84be16..2e5f0fe1f 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -284,8 +284,6 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) ep = _next_free_interrupt_ep(); pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num); assert(ep); - // ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; - // ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; // 0 for epx (double buffered): TODO increase to 1024 for ISO // 2x64 for intep0 // 3x64 for intep1 @@ -295,8 +293,6 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) else { ep = &epx; - // ep->buffer_control = &usbh_dpram->epx_buf_ctrl; - // ep->endpoint_control = &usbh_dpram->epx_ctrl; ep->hw_data_buf = &usbh_dpram->epx_data[0]; } @@ -306,8 +302,6 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval) { // Already has data buffer, endpoint control, and buffer control allocated at this point - // assert(ep->endpoint_control); - // assert(ep->buffer_control); assert(ep->hw_data_buf); uint8_t const num = tu_edpt_number(ep_addr); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 8e7bf2dae..5258b9705 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -88,32 +88,25 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->user_buf = 0; } -void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) { +void __tusb_irq_path_func(hwep_buf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) { const bool is_host = rp2usb_is_host_mode(); - uint32_t value = 0; - io_rw_32 *buf_ctrl; - - #if CFG_TUH_ENABLED - if (is_host) { - buf_ctrl = hwep_buf_ctrl_reg_host(ep); - } else - #endif - { - buf_ctrl = hwep_buf_ctrl_reg_device(ep); - } + uint32_t value = 0; + uint32_t buf_ctrl = *buf_ctrl_reg; if (and_mask) { - value = *buf_ctrl & and_mask; + value = buf_ctrl & and_mask; } if (or_mask) { value |= or_mask; if (or_mask & USB_BUF_CTRL_AVAIL) { - if (*buf_ctrl & USB_BUF_CTRL_AVAIL) { - panic("ep %02X was already available", ep->ep_addr); + if (buf_ctrl & USB_BUF_CTRL_AVAIL) { + panic("buf_ctrl @%lX already available", (uintptr_t)buf_ctrl_reg); } - *buf_ctrl = value & ~USB_BUF_CTRL_AVAIL; - // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control + *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL; + + // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, we need to + // wait at least 1/48 mhz (usb clock), 12 cycles should be good for 48*12Mhz = 576Mhz. // Don't need delay in host mode as host is in charge if (!is_host) { busy_wait_at_least_cycles(12); @@ -121,7 +114,7 @@ void __tusb_irq_path_func(hwep_buf_ctrl_update)(struct hw_endpoint *ep, uint32_t } } - *buf_ctrl = value; + *buf_ctrl_reg = value; } // prepare buffer, move data if tx, return buffer control @@ -161,21 +154,21 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, // Prepare buffer control register value void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - bool is_rx; + bool is_rx; io_rw_32 *ep_ctrl_reg; - // io_rw_32 *buf_ctrl_reg; + io_rw_32 *buf_ctrl_reg; #if CFG_TUH_ENABLED const bool is_host = rp2usb_is_host_mode(); if (is_host) { - // buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); + buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); ep_ctrl_reg = hwep_ctrl_reg_host(ep); is_rx = (dir == TUSB_DIR_IN); } else #endif { - // buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); ep_ctrl_reg = hwep_ctrl_reg_device(ep); is_rx = (dir == TUSB_DIR_OUT); } @@ -216,7 +209,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) // Finally, write to buffer_control which will trigger the transfer // the next time the controller polls this dpram address - hwep_buf_ctrl_set(ep, buf_ctrl); + hwep_buf_ctrl_set(buf_ctrl_reg, buf_ctrl); } void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { @@ -334,7 +327,8 @@ static void __tusb_irq_path_func(hwep_xfer_sync)(hw_endpoint_t *ep) { ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; - hwep_buf_ctrl_set(ep, 0); + io_rw_32 *buf_ctrl_reg = is_host ? hwep_buf_ctrl_reg_host(ep) : hwep_buf_ctrl_reg_device(ep); + hwep_buf_ctrl_set(buf_ctrl_reg, 0); usb_hw->abort &= ~TU_BIT(ep_id); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 2ec00694e..e9fed73de 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -45,41 +45,27 @@ // Hardware information per endpoint typedef struct hw_endpoint { - // Is this a valid struct - bool configured; - uint8_t ep_addr; uint8_t next_pid; + // Interrupt, bulk, etc + uint8_t transfer_type; - // Endpoint control register - // io_rw_32 *endpoint_control; - - // Buffer control register - // io_rw_32 *buffer_control; + bool active; // Endpoint is in use + uint8_t pending; // Transfer scheduled but not active - // Buffer pointer in usb dpram - uint8_t *hw_data_buf; + uint16_t wMaxPacketSize; - // User buffer in main memory - uint8_t *user_buf; + uint8_t *hw_data_buf; // Buffer pointer in usb dpram + uint8_t *user_buf; // User buffer in main memory // Current transfer information uint16_t remaining_len; uint16_t xferred_len; - // Data needed from EP descriptor - uint16_t wMaxPacketSize; - - // Endpoint is in use - bool active; - - // Interrupt, bulk, etc - uint8_t transfer_type; - - // Transfer scheduled but not active - uint8_t pending; - #if CFG_TUH_ENABLED + // Is this a valid struct + bool configured; + // Only needed for host uint8_t dev_addr; @@ -131,7 +117,7 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_device(struct hw #if CFG_TUH_ENABLED TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(struct hw_endpoint *ep) { - if (ep->transfer_type == TUSB_XFER_CONTROL) { + if (tu_edpt_number(ep->ep_addr) == 0) { return &usbh_dpram->epx_ctrl; } return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; @@ -149,18 +135,18 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_host(struct hw_e //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -void hwep_buf_ctrl_update(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask); +void hwep_buf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set(struct hw_endpoint *ep, uint32_t value) { - hwep_buf_ctrl_update(ep, 0, value); +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwep_buf_ctrl_update(buf_ctrl_reg, 0, value); } -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set_mask32(struct hw_endpoint *ep, uint32_t value) { - hwep_buf_ctrl_update(ep, ~value, value); +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwep_buf_ctrl_update(buf_ctrl_reg, ~value, value); } -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_clear_mask(struct hw_endpoint *ep, uint32_t value) { - hwep_buf_ctrl_update(ep, ~value, 0); +TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_clear_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwep_buf_ctrl_update(buf_ctrl_reg, ~value, 0); } static inline uintptr_t hw_data_offset(uint8_t *buf) { -- cgit v1.3.1 From 07416f704f1a55b286d5c9b082e6f1e3e51ce9b0 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 8 Jan 2026 11:33:08 +0700 Subject: minor clean up --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 68 +++++++++++++--------------- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 31 ++++++------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 26 +++++------ src/portable/raspberrypi/rp2040/rp2040_usb.h | 34 +++++++------- 4 files changed, 77 insertions(+), 82 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 5c21590c4..952245cb1 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -71,18 +71,17 @@ TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8 // main processing for dcd_edpt_iso_activate static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - const uint8_t epnum = tu_edpt_number(ep_addr); - ep->ep_addr = ep_addr; ep->next_pid = 0u; ep->wMaxPacketSize = wMaxPacketSize; ep->transfer_type = transfer_type; // Clear existing buffer control state - io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); *buf_ctrl_reg = 0; // allocated hw buffer + const uint8_t epnum = tu_edpt_number(ep_addr); if (epnum == 0) { // Buffer offset is fixed (also double buffered) ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; @@ -104,16 +103,9 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa } } -// Init, allocate buffer and enable endpoint -static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - const uint8_t epnum = tu_edpt_number(ep_addr); - const tusb_dir_t dir = tu_edpt_dir(ep_addr); - hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - - hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type); - - // Set endpoint control register to enable (EP0 has no endpoint control register) +static void hw_endpoint_enable(hw_endpoint_t *ep, uint8_t transfer_type) { io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep); + // Set endpoint control register to enable (EP0 has no endpoint control register) if (ctrl_reg != NULL) { const uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); @@ -121,14 +113,24 @@ static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t } } +// Init and enable endpoint +static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + + hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type); + hw_endpoint_enable(ep, transfer_type); +} + static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { // Abort any pending transfer - // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1). - // Which means we are not guaranteed to safely abort pending transfer on B0 and B1. const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); - const uint8_t epnum = tu_edpt_number(ep->ep_addr); + const uint8_t epnum = tu_edpt_number(ep->ep_addr); const uint32_t abort_mask = TU_BIT((epnum << 1) | (dir ? 0 : 1)); + // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1). + // Which means we are not guaranteed to safely abort pending transfer on B0 and B1. if (rp2040_chip_version() >= 2) { usb_hw_set->abort = abort_mask; while ((usb_hw->abort_done & abort_mask) != abort_mask) {} @@ -139,8 +141,8 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { buf_ctrl |= USB_BUF_CTRL_DATA1_PID; } - io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); - hwep_buf_ctrl_set(buf_ctrl_reg, buf_ctrl); + io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); hw_endpoint_reset_transfer(ep); if (rp2040_chip_version() >= 2) { @@ -265,15 +267,12 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { #if FORCE_VBUS_DETECT == 0 // Since we force VBUS detect On, device will always think it is connected and // couldn't distinguish between disconnect and suspend - if (status & USB_INTS_DEV_CONN_DIS_BITS) - { + if (status & USB_INTS_DEV_CONN_DIS_BITS) { handled |= USB_INTS_DEV_CONN_DIS_BITS; - if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS ) - { + if (usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS) { // Connected: nothing to do - }else - { + } else { // Disconnected dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); } @@ -295,8 +294,10 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX // Only run enumeration workaround if pull up is enabled - if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix(); -#endif + if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) { + rp2040_usb_device_enumeration_fix(); + } + #endif } /* Note from pico datasheet 4.1.2.6.4 (v1.2) @@ -458,7 +459,6 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { (void) rhport; const uint8_t xfer_type = desc_edpt->bmAttributes.xfer; - TU_VERIFY(xfer_type != TUSB_XFER_ISOCHRONOUS); hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type); return true; } @@ -485,13 +485,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) } ep->wMaxPacketSize = ep_desc->wMaxPacketSize; - // Set control register to enable endpoint - io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep); - if (ctrl_reg != NULL) { - const uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | ((uint32_t)TUSB_XFER_ISOCHRONOUS << EP_CTRL_BUFFER_TYPE_LSB) | - hw_data_offset(ep->hw_data_buf); - *ctrl_reg = ctrl_value; - } + hw_endpoint_enable(ep, TUSB_XFER_ISOCHRONOUS); return true; } @@ -522,8 +516,8 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { } // stall and clear current pending buffer, may need to use EP_ABORT - io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); - hwep_buf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL); + io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + hwbuf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -534,8 +528,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // clear stall also reset toggle to DATA0, ready for next transfer ep->next_pid = 0; - io_rw_32 *buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); - hwep_buf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL); + io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + hwbuf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL); } } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 2e5f0fe1f..c4eba0d5f 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -62,22 +62,23 @@ enum { USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS }; -static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) -{ +static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) { uint8_t num = tu_edpt_number(ep_addr); - if ( num == 0 ) return &epx; + if (num == 0) { + return &epx; + } - for ( uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ ) - { + for (uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { struct hw_endpoint *ep = &ep_pool[i]; - if ( ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr) ) return ep; + if (ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr)) { + return ep; + } } return NULL; } -TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) -{ +TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) { return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; } @@ -98,8 +99,7 @@ static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } -static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep) -{ +static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep) { usb_hw_clear->buf_status = bit; // EP may have been stalled? assert(ep->active); @@ -357,8 +357,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t // Finally, enable interrupt that endpoint usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1); - // If it's an interrupt endpoint we need to set up the buffer control - // register + // If it's an interrupt endpoint we need to set up the buffer control register } } @@ -456,8 +455,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // reset epx if it is currently active with unplugged device if (epx.configured && epx.active && epx.dev_addr == dev_addr) { epx.configured = false; - *hwep_ctrl_reg_host(&epx) = 0; - *hwep_buf_ctrl_reg_host(&epx) = 0; + *hwep_ctrl_reg_host(&epx) = 0; + *hwbuf_ctrl_reg_host(&epx) = 0; hw_endpoint_reset_transfer(&epx); } @@ -472,8 +471,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // unconfigure the endpoint ep->configured = false; - *hwep_ctrl_reg_host(ep) = 0; - *hwep_buf_ctrl_reg_host(ep) = 0; + *hwep_ctrl_reg_host(ep) = 0; + *hwbuf_ctrl_reg_host(ep) = 0; hw_endpoint_reset_transfer(ep); } } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 5258b9705..798129e0c 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -88,7 +88,7 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->user_buf = 0; } -void __tusb_irq_path_func(hwep_buf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) { +void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) { const bool is_host = rp2usb_is_host_mode(); uint32_t value = 0; uint32_t buf_ctrl = *buf_ctrl_reg; @@ -101,7 +101,7 @@ void __tusb_irq_path_func(hwep_buf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t value |= or_mask; if (or_mask & USB_BUF_CTRL_AVAIL) { if (buf_ctrl & USB_BUF_CTRL_AVAIL) { - panic("buf_ctrl @%lX already available", (uintptr_t)buf_ctrl_reg); + panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_ctrl_reg); } *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL; @@ -162,15 +162,15 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) #if CFG_TUH_ENABLED const bool is_host = rp2usb_is_host_mode(); if (is_host) { - buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); - ep_ctrl_reg = hwep_ctrl_reg_host(ep); - is_rx = (dir == TUSB_DIR_IN); + buf_ctrl_reg = hwbuf_ctrl_reg_host(ep); + ep_ctrl_reg = hwep_ctrl_reg_host(ep); + is_rx = (dir == TUSB_DIR_IN); } else #endif { - buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); - ep_ctrl_reg = hwep_ctrl_reg_device(ep); - is_rx = (dir == TUSB_DIR_OUT); + buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + ep_ctrl_reg = hwep_ctrl_reg_device(ep); + is_rx = (dir == TUSB_DIR_OUT); } // always compute and start with buffer 0 @@ -209,7 +209,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) // Finally, write to buffer_control which will trigger the transfer // the next time the controller polls this dpram address - hwep_buf_ctrl_set(buf_ctrl_reg, buf_ctrl); + hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); } void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { @@ -287,13 +287,13 @@ static void __tusb_irq_path_func(hwep_xfer_sync)(hw_endpoint_t *ep) { #if CFG_TUH_ENABLED const bool is_host = rp2usb_is_host_mode(); if (is_host) { - buf_ctrl_reg = hwep_buf_ctrl_reg_host(ep); + buf_ctrl_reg = hwbuf_ctrl_reg_host(ep); ep_ctrl_reg = hwep_ctrl_reg_host(ep); is_rx = (dir == TUSB_DIR_IN); } else #endif { - buf_ctrl_reg = hwep_buf_ctrl_reg_device(ep); + buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); ep_ctrl_reg = hwep_ctrl_reg_device(ep); is_rx = (dir == TUSB_DIR_OUT); } @@ -327,8 +327,8 @@ static void __tusb_irq_path_func(hwep_xfer_sync)(hw_endpoint_t *ep) { ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; - io_rw_32 *buf_ctrl_reg = is_host ? hwep_buf_ctrl_reg_host(ep) : hwep_buf_ctrl_reg_device(ep); - hwep_buf_ctrl_set(buf_ctrl_reg, 0); + io_rw_32 *buf_ctrl_reg = is_host ? hwbuf_ctrl_reg_host(ep) : hwbuf_ctrl_reg_device(ep); + hwbuf_ctrl_set(buf_ctrl_reg, 0); usb_hw->abort &= ~TU_BIT(ep_id); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index e9fed73de..368bb6509 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -1,10 +1,6 @@ #ifndef RP2040_COMMON_H_ #define RP2040_COMMON_H_ -#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE) -#error TinyUSB device and host mode not supported at the same time -#endif - #include "common/tusb_common.h" #include "pico.h" @@ -13,6 +9,10 @@ #include "hardware/resets.h" #include "hardware/timer.h" +#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE) + #error TinyUSB device and host mode not supported at the same time +#endif + #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX) #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX #endif @@ -36,6 +36,9 @@ #define __tusb_irq_path_func(x) x #endif +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ #define usb_hw_set ((usb_hw_t *) hw_set_alias_untyped(usb_hw)) #define usb_hw_clear ((usb_hw_t *) hw_clear_alias_untyped(usb_hw)) @@ -56,9 +59,9 @@ typedef struct hw_endpoint uint16_t wMaxPacketSize; uint8_t *hw_data_buf; // Buffer pointer in usb dpram - uint8_t *user_buf; // User buffer in main memory // Current transfer information + uint8_t *user_buf; // User buffer in main memory uint16_t remaining_len; uint16_t xferred_len; @@ -108,7 +111,7 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_end return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out; } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_device(struct hw_endpoint *ep) { +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) { const uint8_t epnum = tu_edpt_number(ep->ep_addr); const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out; @@ -123,30 +126,29 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(struct hw_endpo return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_buf_ctrl_reg_host(struct hw_endpoint *ep) { - if (ep->transfer_type == TUSB_XFER_CONTROL) { +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_host(struct hw_endpoint *ep) { + if (tu_edpt_number(ep->ep_addr) == 0) { return &usbh_dpram->epx_buf_ctrl; } return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; } #endif - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -void hwep_buf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); +void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwep_buf_ctrl_update(buf_ctrl_reg, 0, value); +TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwbuf_ctrl_update(buf_ctrl_reg, 0, value); } -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_set_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwep_buf_ctrl_update(buf_ctrl_reg, ~value, value); +TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwbuf_ctrl_update(buf_ctrl_reg, ~value, value); } -TU_ATTR_ALWAYS_INLINE static inline void hwep_buf_ctrl_clear_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwep_buf_ctrl_update(buf_ctrl_reg, ~value, 0); +TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_clear_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { + hwbuf_ctrl_update(buf_ctrl_reg, ~value, 0); } static inline uintptr_t hw_data_offset(uint8_t *buf) { -- cgit v1.3.1 From d7e715d5c238e9b05ef711a1a73d54c40b37729e Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 8 Jan 2026 15:42:08 +0700 Subject: fix hcd force_single mistake by refactor --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 42 +++++++++------------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 5 ++-- 2 files changed, 16 insertions(+), 31 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index c4eba0d5f..db3a81c87 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -110,35 +110,24 @@ static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_en } } -static void __tusb_irq_path_func(hw_handle_buff_status)(void) -{ +static void __tusb_irq_path_func(handle_hwbuf_status)(void) { uint32_t remaining_buffers = usb_hw->buf_status; pico_trace("buf_status 0x%08lx\n", remaining_buffers); // Check EPX first uint bit = 0b1; - if ( remaining_buffers & bit ) - { + if (remaining_buffers & bit) { remaining_buffers &= ~bit; struct hw_endpoint * ep = &epx; - uint32_t ep_ctrl = *hwep_ctrl_reg_host(ep); - if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS ) - { - TU_LOG(3, "Double Buffered: "); - } - else - { - TU_LOG(3, "Single Buffered: "); - } - TU_LOG_HEX(3, ep_ctrl); + // uint32_t ep_ctrl = *hwep_ctrl_reg_host(ep); + // TU_LOG_HEX(3, ep_ctrl); _handle_buff_status_bit(bit, ep); } // Check "interrupt" (asynchronous) endpoints for both IN and OUT - for ( uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++ ) - { + for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++) { // EPX is bit 0 & 1 // IEP1 IN is bit 2 // IEP1 OUT is bit 3 @@ -147,19 +136,16 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void) // IEP3 IN is bit 6 // IEP3 OUT is bit 7 // etc - for ( uint j = 0; j < 2; j++ ) - { + for (uint j = 0; j < 2; j++) { bit = 1 << (i * 2 + j); - if ( remaining_buffers & bit ) - { + if (remaining_buffers & bit) { remaining_buffers &= ~bit; _handle_buff_status_bit(bit, &ep_pool[i]); } } } - if ( remaining_buffers ) - { + if (remaining_buffers) { panic("Unhandled buffer %d\n", remaining_buffers); } } @@ -220,7 +206,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { handled |= USB_INTS_BUFF_STATUS_BITS; TU_LOG(2, "Buffer complete\r\n"); - hw_handle_buff_status(); + handle_hwbuf_status(); } if ( status & USB_INTS_TRANS_COMPLETE_BITS ) @@ -240,8 +226,8 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) if ( status & USB_INTS_ERROR_DATA_SEQ_BITS ) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; - TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hw_endpoint_get_buf_ctrl(&epx)), - tu_u32_high16(*hw_endpoint_get_buf_ctrl(&epx))); + TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hwbuf_ctrl_reg_host(&epx)), + tu_u32_high16(*hwbuf_ctrl_reg_host(&epx))); panic("Data Seq Error \n"); } @@ -322,10 +308,8 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t assert(!(dpram_offset & 0b111111)); // Fill in endpoint control register with buffer offset - uint32_t ep_reg = EP_CTRL_ENABLE_BITS - | EP_CTRL_INTERRUPT_PER_BUFFER - | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) - | dpram_offset; + uint32_t ep_reg = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; if ( bmInterval ) { ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 798129e0c..9c9255518 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -156,11 +156,12 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); bool is_rx; + bool is_host = false; io_rw_32 *ep_ctrl_reg; io_rw_32 *buf_ctrl_reg; #if CFG_TUH_ENABLED - const bool is_host = rp2usb_is_host_mode(); + is_host = rp2usb_is_host_mode(); if (is_host) { buf_ctrl_reg = hwbuf_ctrl_reg_host(ep); ep_ctrl_reg = hwep_ctrl_reg_host(ep); @@ -185,7 +186,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single // buffered, // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint - const bool force_single = is_rx; + const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0); if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data -- cgit v1.3.1 From 0b970e6a6633f703acb0f40b52b741cf492ff166 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 9 Jan 2026 00:08:43 +0700 Subject: only apply errata E5 and E15 for rp2040. rp2350 already fixes these --- hw/bsp/rp2040/family.cmake | 6 +- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 14 ++-- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 119 ++++++++++----------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 25 +++--- src/portable/raspberrypi/rp2040/rp2040_usb.h | 67 +++++++++------ 5 files changed, 109 insertions(+), 122 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 1602e35eb..9dc28fc70 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -126,7 +126,7 @@ target_compile_definitions(tinyusb_host_base INTERFACE #------------------------------------ # Host MAX3421 -#------------------------------------ +#------------------------------------1 add_library(tinyusb_host_max3421 INTERFACE) target_sources(tinyusb_host_max3421 INTERFACE ${TOP}/src/portable/analog/max3421/hcd_max3421.c @@ -157,10 +157,12 @@ target_link_libraries(tinyusb_bsp INTERFACE # tinyusb_additions will hold our extra settings for examples add_library(tinyusb_additions INTERFACE) +if (PICO_PLATFORM STREQUAL rp2040) target_compile_definitions(tinyusb_additions INTERFACE PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1 PICO_RP2040_USB_DEVICE_UFRAME_FIX=1 -) + ) +endif () if(LOGGER STREQUAL "RTT" OR LOGGER STREQUAL "rtt") target_compile_definitions(tinyusb_additions INTERFACE diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 952245cb1..bdd0d1728 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -92,6 +92,12 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa // double buffered Bulk endpoint if (transfer_type == TUSB_XFER_BULK) { size *= 2u; + + #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + ep->e15_bulk_in = true; + } + #endif } // assign buffer @@ -221,17 +227,14 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF - if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) { + if (ep->e15_bulk_in && ep->active) { keep_sof_alive = true; hw_endpoint_lock_update(ep, 1); - - // Deferred enable? if (ep->pending) { ep->pending = 0; hw_endpoint_start_next_buffer(ep); } - hw_endpoint_lock_update(ep, -1); } } @@ -292,7 +295,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS; -#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX + #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX // Only run enumeration workaround if pull up is enabled if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) { rp2040_usb_device_enumeration_fix(); @@ -491,7 +494,6 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) void dcd_edpt_close_all(uint8_t rhport) { (void) rhport; - // may need to use EP Abort reset_non_control_endpoints(); } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index db3a81c87..1193009ef 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -82,15 +82,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) { return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; } -TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) -{ +TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { // If this device is different to the speed of the root device // (i.e. is a low speed device on a full speed hub) then need pre return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } -static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result) -{ +static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; uint8_t ep_addr = ep->ep_addr; @@ -99,35 +97,28 @@ static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } -static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep) { +static void __tusb_irq_path_func(handle_hhwbuf_status_bit)(uint bit, struct hw_endpoint *ep) { usb_hw_clear->buf_status = bit; - // EP may have been stalled? - assert(ep->active); - bool done = hw_endpoint_xfer_continue(ep); - if ( done ) - { + const bool done = hw_endpoint_xfer_continue(ep); + if (done) { hw_xfer_complete(ep, XFER_RESULT_SUCCESS); } } static void __tusb_irq_path_func(handle_hwbuf_status)(void) { - uint32_t remaining_buffers = usb_hw->buf_status; - pico_trace("buf_status 0x%08lx\n", remaining_buffers); + uint32_t buf_status = usb_hw->buf_status; + pico_trace("buf_status 0x%08lx\n", buf_status); // Check EPX first uint bit = 0b1; - if (remaining_buffers & bit) { - remaining_buffers &= ~bit; + if (buf_status & bit) { + buf_status &= ~bit; struct hw_endpoint * ep = &epx; - - // uint32_t ep_ctrl = *hwep_ctrl_reg_host(ep); - // TU_LOG_HEX(3, ep_ctrl); - - _handle_buff_status_bit(bit, ep); + handle_hhwbuf_status_bit(bit, ep); } // Check "interrupt" (asynchronous) endpoints for both IN and OUT - for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++) { + for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && buf_status; i++) { // EPX is bit 0 & 1 // IEP1 IN is bit 2 // IEP1 OUT is bit 3 @@ -138,15 +129,15 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { // etc for (uint j = 0; j < 2; j++) { bit = 1 << (i * 2 + j); - if (remaining_buffers & bit) { - remaining_buffers &= ~bit; - _handle_buff_status_bit(bit, &ep_pool[i]); + if (buf_status & bit) { + buf_status &= ~bit; + handle_hhwbuf_status_bit(bit, &ep_pool[i]); } } } - if (remaining_buffers) { - panic("Unhandled buffer %d\n", remaining_buffers); + if (buf_status) { + panic("Unhandled buffer %d\n", buf_status); } } @@ -251,7 +242,7 @@ static struct hw_endpoint *_next_free_interrupt_ep(void) ep = &ep_pool[i]; if ( !ep->configured ) { - // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate + // Will be configured by hw_endpoint_init / hw_endpoint_allocate ep->interrupt_num = (uint8_t) (i - 1); return ep; } @@ -259,12 +250,13 @@ static struct hw_endpoint *_next_free_interrupt_ep(void) return ep; } -static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) -{ - struct hw_endpoint * ep = NULL; +static hw_endpoint_t *hw_endpoint_allocate(uint8_t transfer_type) { + hw_endpoint_t *ep = NULL; - if ( transfer_type != TUSB_XFER_CONTROL ) - { + if (transfer_type == TUSB_XFER_CONTROL) { + ep = &epx; + ep->hw_data_buf = &usbh_dpram->epx_data[0]; + } else { // Note: even though datasheet name these "Interrupt" endpoints. These are actually // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation) ep = _next_free_interrupt_ep(); @@ -276,17 +268,12 @@ static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type) // etc ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)]; } - else - { - ep = &epx; - ep->hw_data_buf = &usbh_dpram->epx_data[0]; - } return ep; } -static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval) -{ +static void hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, + uint8_t transfer_type, uint8_t bmInterval) { // Already has data buffer, endpoint control, and buffer control allocated at this point assert(ep->hw_data_buf); @@ -299,7 +286,6 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t // Response to a setup packet on EP0 starts with pid of 1 ep->next_pid = (num == 0 ? 1u : 0u); ep->wMaxPacketSize = wMaxPacketSize; - ep->transfer_type = transfer_type; pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type); pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf); @@ -463,46 +449,33 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { } } -uint32_t hcd_frame_number(uint8_t rhport) -{ - (void) rhport; +uint32_t hcd_frame_number(uint8_t rhport) { + (void)rhport; return usb_hw->sof_rd; } -void hcd_int_enable(uint8_t rhport) -{ - (void) rhport; - assert(rhport == 0); +void hcd_int_enable(uint8_t rhport) { + (void)rhport; irq_set_enabled(USBCTRL_IRQ, true); } -void hcd_int_disable(uint8_t rhport) -{ - (void) rhport; +void hcd_int_disable(uint8_t rhport) { + (void)rhport; // todo we should check this is disabling from the correct core; note currently this is never called - assert(rhport == 0); irq_set_enabled(USBCTRL_IRQ, false); } //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - (void) rhport; - +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { + (void)rhport; pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - - // Allocated differently based on if it's an interrupt endpoint or not - struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer); + hw_endpoint_t *ep = hw_endpoint_allocate(ep_desc->bmAttributes.xfer); TU_ASSERT(ep); - _hw_endpoint_init(ep, - dev_addr, - ep_desc->bEndpointAddress, - tu_edpt_packet_size(ep_desc), - ep_desc->bmAttributes.xfer, - ep_desc->bInterval); + hw_endpoint_init(ep, dev_addr, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer, + ep_desc->bInterval); return true; } @@ -512,13 +485,12 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) -{ +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void) rhport; pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); - uint8_t const ep_num = tu_edpt_number(ep_addr); + const uint8_t ep_num = tu_edpt_number(ep_addr); tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); // Get appropriate ep. Either EPX or interrupt endpoint @@ -530,19 +502,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * assert(!ep->active); // Control endpoint can change direction 0x00 <-> 0x80 - if ( ep_addr != ep->ep_addr ) - { + if (ep_addr != ep->ep_addr) { assert(ep_num == 0); // Direction has flipped on endpoint control so re init it but with same properties - _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0); + hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, TUSB_XFER_CONTROL, 0); } // If a normal transfer (non-interrupt) then initiate using // sie ctrl registers. Otherwise, interrupt ep registers should // already be configured - if ( ep == &epx ) - { + if (ep == &epx) { hw_endpoint_xfer_start(ep, buffer, buflen); // That has set up buffer control, endpoint control etc @@ -558,8 +528,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS; busy_wait_at_least_cycles(12); usb_hw->sie_ctrl = flags; - }else - { + } else { hw_endpoint_xfer_start(ep, buffer, buflen); } @@ -584,14 +553,14 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet } // Configure EP0 struct with setup info for the trans complete - struct hw_endpoint * ep = _hw_endpoint_allocate( (uint8_t) TUSB_XFER_CONTROL); + hw_endpoint_t *ep = hw_endpoint_allocate((uint8_t)TUSB_XFER_CONTROL); TU_ASSERT(ep); // EPX should be inactive assert(!ep->active); // EP0 out - _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); + hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); assert(ep->configured); ep->remaining_len = 8; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 9c9255518..8f91ecf22 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -38,10 +38,8 @@ static void hwep_xfer_sync(hw_endpoint_t *ep); #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX -static bool e15_is_bulkin_ep(struct hw_endpoint *ep); static bool e15_is_critical_frame_period(struct hw_endpoint *ep); #else - #define e15_is_bulkin_ep(x) (false) #define e15_is_critical_frame_period(x) (false) #endif @@ -228,13 +226,16 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to ep->active = true; ep->user_buf = buffer; - if (e15_is_bulkin_ep(ep)) { + #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + if (ep->e15_bulk_in) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; } if (e15_is_critical_frame_period(ep)) { - ep->pending = 1; - } else { + ep->pending = 1; // skip transfer if we are in critical frame period + } else + #endif + { hw_endpoint_start_next_buffer(ep); } @@ -360,9 +361,12 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { hw_endpoint_lock_update(ep, -1); return true; } else { + #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX if (e15_is_critical_frame_period(ep)) { ep->pending = 1; - } else { + } else + #endif + { hw_endpoint_start_next_buffer(ep); } } @@ -397,15 +401,12 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { volatile uint32_t e15_last_sof = 0; -// check if Errata 15 is needed for this endpoint i.e device bulk-in -static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint *ep) { - return (!rp2usb_is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN && ep->transfer_type == TUSB_XFER_BULK); -} - // check if we need to apply Errata 15 workaround : i.e // Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) { - TU_VERIFY(e15_is_bulkin_ep(ep)); + if (!ep->e15_bulk_in) { + return false; + } /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point. * The device state machine cannot recover from receiving an incorrect PID diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 368bb6509..944d604bc 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -13,17 +13,30 @@ #error TinyUSB device and host mode not supported at the same time #endif -#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX) -#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX +// E5 and E15 only apply to RP2040 +#if defined(PICO_RP2040) && PICO_RP2040 == 1 + // RP2040 E5: USB device fails to exit RESET state on busy USB bus. + #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX) + #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX + #endif + + // RP2040 E15: USB Device controller will hang if certain bus errors occur during an IN transfer. + #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) + #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX + #endif #endif -#if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) -#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX +#ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX + #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX 0 +#endif + +#ifndef TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 0 #endif #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX -#undef PICO_RP2040_USB_FAST_IRQ -#define PICO_RP2040_USB_FAST_IRQ 1 + #undef PICO_RP2040_USB_FAST_IRQ + #define PICO_RP2040_USB_FAST_IRQ 1 #endif #ifndef PICO_RP2040_USB_FAST_IRQ @@ -46,36 +59,36 @@ #define pico_trace(...) TU_LOG(3, __VA_ARGS__) // Hardware information per endpoint -typedef struct hw_endpoint -{ - uint8_t ep_addr; - uint8_t next_pid; - // Interrupt, bulk, etc - uint8_t transfer_type; +typedef struct hw_endpoint { + uint8_t ep_addr; + uint8_t next_pid; + uint8_t transfer_type; - bool active; // Endpoint is in use - uint8_t pending; // Transfer scheduled but not active + bool active; // transferring data - uint16_t wMaxPacketSize; +#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + bool e15_bulk_in; // Errata15 device bulk in + uint8_t pending; // Transfer scheduled but not active +#endif - uint8_t *hw_data_buf; // Buffer pointer in usb dpram + uint16_t wMaxPacketSize; + uint8_t *hw_data_buf; // Buffer pointer in usb dpram - // Current transfer information - uint8_t *user_buf; // User buffer in main memory - uint16_t remaining_len; - uint16_t xferred_len; + // Current transfer information + uint8_t *user_buf; // User buffer in main memory + uint16_t remaining_len; + uint16_t xferred_len; #if CFG_TUH_ENABLED - // Is this a valid struct - bool configured; + // Is this a valid struct + bool configured; - // Only needed for host - uint8_t dev_addr; + // Only needed for host + uint8_t dev_addr; - // If interrupt endpoint - uint8_t interrupt_num; + // If interrupt endpoint + uint8_t interrupt_num; #endif - } hw_endpoint_t; #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX -- cgit v1.3.1 From 8452dfe3055c876a1c8cb1f52c7bceeae6678452 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 9 Jan 2026 10:28:27 +0700 Subject: clean up --- .idea/debugServers/mcxa153.xml | 13 ++++++++++ .idea/debugServers/ra6m1.xml | 13 ++++++++++ .idea/debugServers/ra6m5.xml | 13 ++++++++++ .idea/debugServers/rt1011.xml | 13 ++++++++++ .idea/debugServers/rt1170.xml | 13 ++++++++++ .idea/debugServers/stm32f072.xml | 13 ++++++++++ .idea/debugServers/stm32f303.xml | 13 ++++++++++ .idea/debugServers/stm32l053.xml | 13 ++++++++++ hw/bsp/rp2040/family.cmake | 2 +- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 4 ++-- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 36 +++++++++++++--------------- src/tusb_option.h | 2 +- 12 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 .idea/debugServers/mcxa153.xml create mode 100644 .idea/debugServers/ra6m1.xml create mode 100644 .idea/debugServers/ra6m5.xml create mode 100644 .idea/debugServers/rt1011.xml create mode 100644 .idea/debugServers/rt1170.xml create mode 100644 .idea/debugServers/stm32f072.xml create mode 100644 .idea/debugServers/stm32f303.xml create mode 100644 .idea/debugServers/stm32l053.xml (limited to 'src/portable/raspberrypi') diff --git a/.idea/debugServers/mcxa153.xml b/.idea/debugServers/mcxa153.xml new file mode 100644 index 000000000..0e493cbd5 --- /dev/null +++ b/.idea/debugServers/mcxa153.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/ra6m1.xml b/.idea/debugServers/ra6m1.xml new file mode 100644 index 000000000..17c902ec4 --- /dev/null +++ b/.idea/debugServers/ra6m1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/ra6m5.xml b/.idea/debugServers/ra6m5.xml new file mode 100644 index 000000000..d8dfbdeeb --- /dev/null +++ b/.idea/debugServers/ra6m5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/rt1011.xml b/.idea/debugServers/rt1011.xml new file mode 100644 index 000000000..b4be501bd --- /dev/null +++ b/.idea/debugServers/rt1011.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/rt1170.xml b/.idea/debugServers/rt1170.xml new file mode 100644 index 000000000..9a564ed57 --- /dev/null +++ b/.idea/debugServers/rt1170.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/stm32f072.xml b/.idea/debugServers/stm32f072.xml new file mode 100644 index 000000000..c56f5a8fb --- /dev/null +++ b/.idea/debugServers/stm32f072.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/stm32f303.xml b/.idea/debugServers/stm32f303.xml new file mode 100644 index 000000000..84aafc1e3 --- /dev/null +++ b/.idea/debugServers/stm32f303.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/stm32l053.xml b/.idea/debugServers/stm32l053.xml new file mode 100644 index 000000000..5365189cc --- /dev/null +++ b/.idea/debugServers/stm32l053.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 9dc28fc70..40eee082d 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -126,7 +126,7 @@ target_compile_definitions(tinyusb_host_base INTERFACE #------------------------------------ # Host MAX3421 -#------------------------------------1 +#------------------------------------ add_library(tinyusb_host_max3421 INTERFACE) target_sources(tinyusb_host_max3421 INTERFACE ${TOP}/src/portable/analog/max3421/hcd_max3421.c diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index bdd0d1728..71d5cf19c 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -39,9 +39,9 @@ #include "device/dcd.h" // Current implementation force vbus detection as always present, causing device think it is always plugged into host. -// Therefore it cannot detect disconnect event, mistaken it as suspend. +// Therefore, it cannot detect disconnect event, mistaken it as suspend. // Note: won't work if change to 0 (for now) -#define FORCE_VBUS_DETECT 1 + #define FORCE_VBUS_DETECT 1 #define USB_INTS_ERROR_BITS \ (USB_INTS_ERROR_DATA_SEQ_BITS | USB_INTS_ERROR_BIT_STUFF_BITS | USB_INTS_ERROR_CRC_BITS | \ diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 1193009ef..7bf247ced 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -97,7 +97,7 @@ static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } -static void __tusb_irq_path_func(handle_hhwbuf_status_bit)(uint bit, struct hw_endpoint *ep) { +static void __tusb_irq_path_func(handle_hwbuf_status_bit)(uint bit, struct hw_endpoint *ep) { usb_hw_clear->buf_status = bit; const bool done = hw_endpoint_xfer_continue(ep); if (done) { @@ -110,11 +110,11 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { pico_trace("buf_status 0x%08lx\n", buf_status); // Check EPX first - uint bit = 0b1; + uint32_t bit = 1u; if (buf_status & bit) { buf_status &= ~bit; struct hw_endpoint * ep = &epx; - handle_hhwbuf_status_bit(bit, ep); + handle_hwbuf_status_bit(bit, ep); } // Check "interrupt" (asynchronous) endpoints for both IN and OUT @@ -131,7 +131,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { bit = 1 << (i * 2 + j); if (buf_status & bit) { buf_status &= ~bit; - handle_hhwbuf_status_bit(bit, &ep_pool[i]); + handle_hwbuf_status_bit(bit, &ep_pool[i]); } } } @@ -287,39 +287,37 @@ static void hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t e ep->next_pid = (num == 0 ? 1u : 0u); ep->wMaxPacketSize = wMaxPacketSize; - pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type); + pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, transfer_type); pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf); uint dpram_offset = hw_data_offset(ep->hw_data_buf); // Bits 0-5 should be 0 assert(!(dpram_offset & 0b111111)); // Fill in endpoint control register with buffer offset - uint32_t ep_reg = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; - if ( bmInterval ) - { - ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + if (bmInterval) { + ctrl_value |= (uint32_t)((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); } - *hwep_ctrl_reg_host(ep) = ep_reg; - // pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg); + + io_rw_32 *ctrl_reg = hwep_ctrl_reg_host(ep); + *ctrl_reg = ctrl_value; + pico_trace("endpoint control (0x%p) <- 0x%lx\n", ctrl_reg, ctrl_value); ep->configured = true; - if ( ep != &epx ) - { + if (ep != &epx) { // Endpoint has its own addr_endp and interrupt bits to be setup! // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with: // - device address // - endpoint number / direction // - preamble - uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB)); + uint32_t reg = (uint32_t)(dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB)); - if ( dir == TUSB_DIR_OUT ) - { + if (dir == TUSB_DIR_OUT) { reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS; } - if ( need_pre(dev_addr) ) - { + if (need_pre(dev_addr)) { reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; } usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg; diff --git a/src/tusb_option.h b/src/tusb_option.h index 08a0ba2ef..9b0da3ac7 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -335,7 +335,7 @@ #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 #endif -//------------- pio-usb -------------// +//------------- Raspberry Pi -------------// // Enable PIO-USB software host controller #ifndef CFG_TUH_RPI_PIO_USB #define CFG_TUH_RPI_PIO_USB 0 -- cgit v1.3.1 From 4c86cbdc822ccf0f71a67c5f2d627e18ecbad46f Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 9 Jan 2026 18:40:55 +0700 Subject: enable dedicated hwfifo for rp2 --- src/common/tusb_fifo.c | 76 +++++++++++++++++++------- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 10 +++- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 4 +- src/portable/raspberrypi/rp2040/rp2040_usb.c | 55 +++++++++++++------ src/portable/raspberrypi/rp2040/rp2040_usb.h | 35 ++++++------ src/tusb_option.h | 79 +++++++++++++++------------- 6 files changed, 169 insertions(+), 90 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index a92435912..9f188f296 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -33,7 +33,7 @@ // Suppress IAR warning // Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement #if defined(__ICCARM__) -#pragma diag_suppress = Pa082 + #pragma diag_suppress = Pa082 #endif #if OSAL_MUTEX_REQUIRED @@ -110,8 +110,9 @@ void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { } //--------------------------------------------------------------------+ -// Pull & Push -// copy data to/from fifo without updating read/write pointers +// Hardware FIFO API +// Support different data access width and address increment scheme +// Can support multiple i.e both 16 and 32-bit data access if needed //--------------------------------------------------------------------+ #if CFG_TUSB_FIFO_HWFIFO_API #if CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE @@ -122,18 +123,31 @@ void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { #define HWFIFO_ADDR_NEXT(_hwfifo, _const) HWFIFO_ADDR_NEXT_N(_hwfifo, _const, CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE) +//------------- Write -------------// #ifndef CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE -static inline void stride_write(volatile void *hwfifo, const void *src, uint8_t data_stride) { +TU_ATTR_ALWAYS_INLINE static inline void stride_write(volatile void *hwfifo, const void *src, uint8_t data_stride) { + (void)data_stride; // possible unused #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4 - if (data_stride == 4) { + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 4 + if (data_stride == 4) + #endif + { *((volatile uint32_t *)hwfifo) = tu_unaligned_read32(src); } - #endif - #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2 - if (data_stride == 2) { + #endif + + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2 + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 2 + if (data_stride == 2) + #endif + { *((volatile uint16_t *)hwfifo) = tu_unaligned_read16(src); } - #endif + #endif + + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 + *((volatile uint8_t *)hwfifo) = *(const uint8_t *)src; + #endif } // Copy from fifo to fixed address buffer (usually a tx register) with TU_FIFO_FIXED_ADDR_RW32 mode @@ -147,7 +161,8 @@ void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, co HWFIFO_ADDR_NEXT(hwfifo, ); } - #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE > 1 + #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS // 16-bit access is allowed for odd bytes if (len >= 2) { *((volatile uint16_t *)hwfifo) = tu_unaligned_read16(src); @@ -155,16 +170,16 @@ void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, co len -= 2; HWFIFO_ADDR_NEXT_N(hwfifo, , 2); } - #endif + #endif - #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS + #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS // 8-bit access is allowed for odd bytes while (len > 0) { *((volatile uint8_t *)hwfifo) = *src++; len--; HWFIFO_ADDR_NEXT_N(hwfifo, , 1); } - #else + #else // Write odd bytes i.e 1 byte for 16 bit or 1-3 bytes for 32 bit if (len > 0) { @@ -173,13 +188,16 @@ void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, co stride_write(hwfifo, &tmp, data_stride); HWFIFO_ADDR_NEXT(hwfifo, ); } + #endif #endif } #endif +//------------- Read -------------// #ifndef CFG_TUSB_FIFO_HWFIFO_CUSTOM_READ -static inline void stride_read(const volatile void *hwfifo, void *dest, uint8_t data_stride) { +TU_ATTR_ALWAYS_INLINE static inline void stride_read(const volatile void *hwfifo, void *dest, uint8_t data_stride) { (void)data_stride; // possible unused + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4 #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 4 if (data_stride == 4) @@ -197,6 +215,10 @@ static inline void stride_read(const volatile void *hwfifo, void *dest, uint8_t tu_unaligned_write16(dest, *((const volatile uint16_t *)hwfifo)); } #endif + + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 + *(uint8_t *)dest = *((const volatile uint8_t *)hwfifo); + #endif } void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, const tu_hwfifo_access_t *access_mode) { @@ -209,7 +231,8 @@ void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, co HWFIFO_ADDR_NEXT(hwfifo, const); } - #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE > 1 + #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS // 16-bit access is allowed for odd bytes if (len >= 2) { tu_unaligned_write16(dest, *((const volatile uint16_t *)hwfifo)); @@ -235,6 +258,7 @@ void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, co HWFIFO_ADDR_NEXT(hwfifo, const); } #endif + #endif } #endif @@ -251,7 +275,11 @@ static void hwff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uin tu_hwfifo_read(hwfifo, ff_buf, n, access_mode); } else { // Wrap around case - + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 + tu_hwfifo_read(hwfifo, ff_buf, lin_bytes, access_mode); // linear part + HWFIFO_ADDR_NEXT_N(hwfifo, const, lin_bytes); + tu_hwfifo_read(hwfifo, f->buffer, wrap_bytes, access_mode); // wrapped part + #else // Write full words to linear part of buffer const uint8_t data_stride = access_mode->data_stride; const uint32_t odd_mask = data_stride - 1; @@ -286,6 +314,7 @@ static void hwff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uin if (wrap_bytes > 0) { tu_hwfifo_read(hwfifo, ff_buf, wrap_bytes, access_mode); } + #endif } } @@ -303,11 +332,15 @@ static void hwff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t tu_hwfifo_write(hwfifo, ff_buf, n, access_mode); } else { // Wrap around case - + #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 + tu_hwfifo_write(hwfifo, ff_buf, lin_bytes, access_mode); // linear part + HWFIFO_ADDR_NEXT_N(hwfifo, , lin_bytes); + tu_hwfifo_write(hwfifo, f->buffer, wrap_bytes, access_mode); // wrapped part + #else // 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 = lin_bytes & ~odd_mask; tu_hwfifo_write(hwfifo, ff_buf, lin_even, access_mode); HWFIFO_ADDR_NEXT_N(hwfifo, , lin_even); ff_buf += lin_even; @@ -338,10 +371,15 @@ static void hwff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t if (wrap_bytes > 0) { tu_hwfifo_write(hwfifo, ff_buf, wrap_bytes, access_mode); } + #endif } } #endif +//--------------------------------------------------------------------+ +// Pull & Push +// copy data to/from fifo without updating read/write pointers +//--------------------------------------------------------------------+ // send n items to fifo WITHOUT updating write pointer static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr) { uint16_t lin_bytes = f->depth - wr_ptr; @@ -787,6 +825,6 @@ void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { } else { info->linear.len = f->depth - wr_ptr; info->wrapped.len = remain - info->linear.len; // Remaining length - n already was limited to remain or FIFO depth - info->wrapped.ptr = f->buffer; // Always start of buffer + info->wrapped.ptr = f->buffer; // Always start of buffer } } diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 71d5cf19c..f25d808d4 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -502,7 +502,15 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to (void)rhport; (void)is_isr; hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_xfer_start(ep, buffer, total_bytes); + hw_endpoint_xfer_start(ep, buffer, NULL, total_bytes); + return true; +} + +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) { + (void)rhport; + (void)is_isr; + hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_xfer_start(ep, NULL, ff, total_bytes); return true; } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 7bf247ced..06c0ce340 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -511,7 +511,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b // sie ctrl registers. Otherwise, interrupt ep registers should // already be configured if (ep == &epx) { - hw_endpoint_xfer_start(ep, buffer, buflen); + hw_endpoint_xfer_start(ep, buffer, NULL, buflen); // That has set up buffer control, endpoint control etc // for host we have to initiate the transfer @@ -527,7 +527,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b busy_wait_at_least_cycles(12); usb_hw->sie_ctrl = flags; } else { - hw_endpoint_xfer_start(ep, buffer, buflen); + hw_endpoint_xfer_start(ep, buffer, NULL, buflen); } return true; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 8f91ecf22..bde45db1b 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -35,7 +35,7 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ -static void hwep_xfer_sync(hw_endpoint_t *ep); +static void sync_xfer(hw_endpoint_t *ep); #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX static bool e15_is_critical_frame_period(struct hw_endpoint *ep); @@ -55,6 +55,16 @@ static void unaligned_memcpy(void *dst, const void *src, size_t n) { } } +void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) { + (void)access_mode; + unaligned_memcpy((void *)(uintptr_t)hwfifo, src, len); +} + +void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, const tu_hwfifo_access_t *access_mode) { + (void)access_mode; + unaligned_memcpy(dest, (const void *)(uintptr_t)hwfifo, len); +} + void rp2usb_init(void) { // Reset usb controller reset_block(RESETS_RESET_USBCTRL_BITS); @@ -127,9 +137,16 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, ep->next_pid ^= 1u; if (!is_rx) { - // Copy data from user buffer to hw buffer - unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); - ep->user_buf += buflen; + if (buflen) { + // Copy data from user buffer/fifo to hw buffer + uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + if (ep->is_xfer_fifo) { + tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL); + } else { + unaligned_memcpy(hw_buf, ep->user_buf, buflen); + ep->user_buf += buflen; + } + } // Mark as full buf_ctrl |= USB_BUF_CTRL_FULL; @@ -152,7 +169,6 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, // Prepare buffer control register value void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - bool is_rx; bool is_host = false; io_rw_32 *ep_ctrl_reg; @@ -211,7 +227,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); } -void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { +void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { hw_endpoint_lock_update(ep, 1); if (ep->active) { @@ -224,7 +240,14 @@ void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t to ep->remaining_len = total_len; ep->xferred_len = 0; ep->active = true; - ep->user_buf = buffer; + + if (ff != NULL) { + ep->user_fifo = ff; + ep->is_xfer_fifo = true; + } else { + ep->user_buf = buffer; + ep->is_xfer_fifo = false; + } #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX if (ep->e15_bulk_in) { @@ -256,17 +279,20 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 // We are continuing a transfer here. If we are TX, we have successfully // sent some data can increase the length we have sent assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); - - ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); } else { // If we have received some data, so can increase the length // we have received AFTER we have copied it to the user buffer at the appropriate offset assert(buf_ctrl & USB_BUF_CTRL_FULL); - unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes); - ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); - ep->user_buf += xferred_bytes; + uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + if (ep->is_xfer_fifo) { + tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL); + } else { + unaligned_memcpy(ep->user_buf, hw_buf, xferred_bytes); + ep->user_buf += xferred_bytes; + } } + ep->xferred_len += xferred_bytes; // Short packet if (xferred_bytes < ep->wMaxPacketSize) { @@ -278,7 +304,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 } // Update hw endpoint struct with info from hardware after a buff status interrupt -static void __tusb_irq_path_func(hwep_xfer_sync)(hw_endpoint_t *ep) { +static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep) { // const uint8_t ep_num = tu_edpt_number(ep->ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); @@ -350,8 +376,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); } - // Update EP struct from hardware state - hwep_xfer_sync(ep); + sync_xfer(ep); // Update EP struct from hardware state // Now we have synced our state with the hardware. Is there more data to transfer? // If we are done then notify tinyusb diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 944d604bc..cce868540 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -1,14 +1,16 @@ #ifndef RP2040_COMMON_H_ #define RP2040_COMMON_H_ -#include "common/tusb_common.h" - #include "pico.h" #include "hardware/structs/usb.h" #include "hardware/irq.h" #include "hardware/resets.h" #include "hardware/timer.h" +#include "common/tusb_common.h" +#include "osal/osal.h" +#include "common/tusb_fifo.h" + #if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE) #error TinyUSB device and host mode not supported at the same time #endif @@ -63,32 +65,31 @@ typedef struct hw_endpoint { uint8_t ep_addr; uint8_t next_pid; uint8_t transfer_type; - - bool active; // transferring data + bool active; // transferring data + bool is_xfer_fifo; // transfer using fifo #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX bool e15_bulk_in; // Errata15 device bulk in uint8_t pending; // Transfer scheduled but not active #endif +#if CFG_TUH_ENABLED + bool configured; // Is this a valid struct + uint8_t dev_addr; + uint8_t interrupt_num; // for host interrupt endpoints +#endif + uint16_t wMaxPacketSize; uint8_t *hw_data_buf; // Buffer pointer in usb dpram - // Current transfer information - uint8_t *user_buf; // User buffer in main memory + // transfer info + union { + uint8_t *user_buf; // User buffer in main memory + tu_fifo_t *user_fifo; + }; uint16_t remaining_len; uint16_t xferred_len; -#if CFG_TUH_ENABLED - // Is this a valid struct - bool configured; - - // Only needed for host - uint8_t dev_addr; - - // If interrupt endpoint - uint8_t interrupt_num; -#endif } hw_endpoint_t; #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX @@ -102,7 +103,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; } -void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len); +void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); bool hw_endpoint_xfer_continue(struct hw_endpoint *ep); void hw_endpoint_reset_transfer(struct hw_endpoint *ep); void hw_endpoint_start_next_buffer(struct hw_endpoint *ep); diff --git a/src/tusb_option.h b/src/tusb_option.h index e0a52593f..abf5e0608 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -272,6 +272,25 @@ // USBIP //--------------------------------------------------------------------+ +//------------- ChipIdea -------------// +// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is +// insufficient). +#ifndef CFG_TUD_CI_HS_VBUS_CHARGE + #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT + #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0 + #endif + #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT +#endif + +// CI_HS support FIFO transfer if endpoint buffer is 4k aligned and size is multiple of 4k, also DCACHE is disabled +#ifndef CFG_TUD_CI_HS_EPBUF_4K_ALIGNED + #define CFG_TUD_CI_HS_EPBUF_4K_ALIGNED 0 +#endif + +#if CFG_TUD_CI_HS_EPBUF_4K_ALIGNED && !CFG_TUD_MEM_DCACHE_ENABLE + #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 +#endif + //------------- DWC2 -------------// // DMA mode for device #ifndef CFG_TUD_DWC2_DMA_ENABLE @@ -317,41 +336,6 @@ #define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 0 // fixed hwfifo address #endif -//------------- ChipIdea -------------// -// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is -// insufficient). -#ifndef CFG_TUD_CI_HS_VBUS_CHARGE - #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT - #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0 - #endif - #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT -#endif - -// CI_HS support FIFO transfer if endpoint buffer is 4k aligned and size is multiple of 4k, also DCACHE is disabled -#ifndef CFG_TUD_CI_HS_EPBUF_4K_ALIGNED - #define CFG_TUD_CI_HS_EPBUF_4K_ALIGNED 0 -#endif - -#if CFG_TUD_CI_HS_EPBUF_4K_ALIGNED && !CFG_TUD_MEM_DCACHE_ENABLE - #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 -#endif - -//------------- Raspberry Pi -------------// -// Enable PIO-USB software host controller -#ifndef CFG_TUH_RPI_PIO_USB - #define CFG_TUH_RPI_PIO_USB 0 -#endif - -#ifndef CFG_TUD_RPI_PIO_USB - #define CFG_TUD_RPI_PIO_USB 0 -#endif - -//------------ MAX3421 -------------// -// Enable MAX3421 USB host controller -#ifndef CFG_TUH_MAX3421 - #define CFG_TUH_MAX3421 0 -#endif - //------------ FSDEV --------------// #if defined(TUP_USBIP_FSDEV) #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 @@ -368,6 +352,12 @@ #endif #endif +//------------ MAX3421 -------------// +// Enable MAX3421 USB host controller +#ifndef CFG_TUH_MAX3421 + #define CFG_TUH_MAX3421 0 +#endif + //------------ MUSB --------------// #if defined(TUP_USBIP_MUSB) #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 @@ -375,13 +365,30 @@ #define CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS // allow odd 16bit access #define CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS // allow odd 8bit access #define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 0 // fixed hwfifo +#endif + +//------------- Raspberry Pi -------------// +// Enable PIO-USB software host controller +#ifndef CFG_TUH_RPI_PIO_USB + #define CFG_TUH_RPI_PIO_USB 0 +#endif + +#ifndef CFG_TUD_RPI_PIO_USB + #define CFG_TUD_RPI_PIO_USB 0 +#endif +#if (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB + #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 + #define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE 1 + #define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 1 + #define CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE + #define CFG_TUSB_FIFO_HWFIFO_CUSTOM_READ #endif //------------ RUSB2 --------------// #if defined(TUP_USBIP_RUSB2) #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 - #define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE (2 | (TUD_OPT_HIGH_SPEED ? 4 : 0)) // 16 bit and 32 bit data if highspeed + #define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE (2 | (TUD_OPT_HIGH_SPEED ? 4 : 0)) // 16 bit and 32 bit if highspeed #define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 0 #define CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE // custom write since rusb2 can change access width 32 -> 16 and can write // odd byte with byte access -- cgit v1.3.1 From 09b8f4008450e55caea2f8ee559a3d0abcd87a36 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 9 Jan 2026 23:34:00 +0700 Subject: remove transfer_type from hw_endpoint --- hw/bsp/rp2040/family.cmake | 1 + src/portable/raspberrypi/rp2040/dcd_rp2040.c | 1 - src/portable/raspberrypi/rp2040/rp2040_usb.c | 12 ++++++------ src/portable/raspberrypi/rp2040/rp2040_usb.h | 1 - 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 40eee082d..e617ab3ca 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -251,6 +251,7 @@ function(family_configure_target TARGET RTOS) family_flash_jlink(${TARGET}) # Generate linkermap target and post build. LINKERMAP_OPTION can be set with -D to change default options + family_add_bloaty(${TARGET}) family_add_linkermap(${TARGET}) endfunction() diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index f25d808d4..240e6c727 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -74,7 +74,6 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa ep->ep_addr = ep_addr; ep->next_pid = 0u; ep->wMaxPacketSize = wMaxPacketSize; - ep->transfer_type = transfer_type; // Clear existing buffer control state io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index bde45db1b..3b65f57a4 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -47,22 +47,20 @@ static bool e15_is_critical_frame_period(struct hw_endpoint *ep); // Implementation //--------------------------------------------------------------------+ // Provide own byte by byte memcpy as not all copies are aligned -static void unaligned_memcpy(void *dst, const void *src, size_t n) { - uint8_t *dst_byte = (uint8_t*)dst; - const uint8_t *src_byte = (const uint8_t*)src; +static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) { while (n--) { - *dst_byte++ = *src_byte++; + *dst++ = *src++; } } void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) { (void)access_mode; - unaligned_memcpy((void *)(uintptr_t)hwfifo, src, len); + unaligned_memcpy((uint8_t *)(uintptr_t)hwfifo, src, len); } void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, const tu_hwfifo_access_t *access_mode) { (void)access_mode; - unaligned_memcpy(dest, (const void *)(uintptr_t)hwfifo, len); + unaligned_memcpy(dest, (const uint8_t *)(uintptr_t)hwfifo, len); } void rp2usb_init(void) { @@ -141,6 +139,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, // Copy data from user buffer/fifo to hw buffer uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; if (ep->is_xfer_fifo) { + // not in sram, may mess up timing with E15 workaround tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL); } else { unaligned_memcpy(hw_buf, ep->user_buf, buflen); @@ -286,6 +285,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; if (ep->is_xfer_fifo) { + // not in sram, may mess up timing with E15 workaround tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL); } else { unaligned_memcpy(ep->user_buf, hw_buf, xferred_bytes); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index cce868540..c03dc34b2 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -64,7 +64,6 @@ typedef struct hw_endpoint { uint8_t ep_addr; uint8_t next_pid; - uint8_t transfer_type; bool active; // transferring data bool is_xfer_fifo; // transfer using fifo -- cgit v1.3.1 From 0f3c90b445e8d28b7925b629157ffe22b5d385ff Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 15 Jan 2026 10:53:06 +0700 Subject: draft: hcd control work --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 266 +++++++++++++-------------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 5 +- 2 files changed, 126 insertions(+), 145 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 06c0ce340..8e3379622 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -56,21 +56,33 @@ static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, ""); static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS]; #define epx (ep_pool[0]) +static hw_endpoint_t *ep_active = NULL; + // Flags we set by default in sie_ctrl (we add other bits on top) enum { SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS }; -static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) { - uint8_t num = tu_edpt_number(ep_addr); - if (num == 0) { - return &epx; +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +static hw_endpoint_t *edpt_alloc(void) { + for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->wMaxPacketSize == 0) { + return ep; + } } + return NULL; +} - for (uint32_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { +static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { + for (uint32_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { struct hw_endpoint *ep = &ep_pool[i]; - if (ep->configured && (ep->dev_addr == dev_addr) && (ep->ep_addr == ep_addr)) { + if ((ep->dev_addr == daddr) && (ep->wMaxPacketSize > 0) && + (ep->ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->ep_addr) == 0))) { return ep; } } @@ -78,6 +90,10 @@ static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr) { return NULL; } +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void) { return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB; } @@ -113,7 +129,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { uint32_t bit = 1u; if (buf_status & bit) { buf_status &= ~bit; - struct hw_endpoint * ep = &epx; + hw_endpoint_t *ep = ep_active; handle_hwbuf_status_bit(bit, ep); } @@ -141,91 +157,58 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } } -static void __tusb_irq_path_func(hw_trans_complete)(void) -{ - if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) - { - pico_trace("Sent setup packet\n"); - struct hw_endpoint *ep = &epx; - assert(ep->active); - // Set transferred length to 8 for a setup packet +static void __tusb_irq_path_func(hw_trans_complete)(void) { + if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { + hw_endpoint_t *ep = ep_active; ep->xferred_len = 8; hw_xfer_complete(ep, XFER_RESULT_SUCCESS); - } - else - { + } else { // Don't care. Will handle this in buff status return; } } -static void __tusb_irq_path_func(hcd_rp2040_irq)(void) -{ - uint32_t status = usb_hw->ints; - uint32_t handled = 0; +static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { + const uint32_t status = usb_hw->ints; - if ( status & USB_INTS_HOST_CONN_DIS_BITS ) - { - handled |= USB_INTS_HOST_CONN_DIS_BITS; - - if ( dev_speed() ) - { + if (status & USB_INTS_HOST_CONN_DIS_BITS) { + if (dev_speed()) { hcd_event_device_attach(RHPORT_NATIVE, true); - } - else - { + } else { hcd_event_device_remove(RHPORT_NATIVE, true); } - // Clear speed change interrupt usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; } - if ( status & USB_INTS_STALL_BITS ) - { + if (status & USB_INTS_STALL_BITS) { // We have rx'd a stall from the device // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS // AND TRANS_COMPLETE as the stall is an alternative response // to one of those events - pico_trace("Stall REC\n"); - handled |= USB_INTS_STALL_BITS; usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; hw_xfer_complete(&epx, XFER_RESULT_STALLED); } - if ( status & USB_INTS_BUFF_STATUS_BITS ) - { - handled |= USB_INTS_BUFF_STATUS_BITS; - TU_LOG(2, "Buffer complete\r\n"); + if (status & USB_INTS_BUFF_STATUS_BITS) { handle_hwbuf_status(); } - if ( status & USB_INTS_TRANS_COMPLETE_BITS ) - { - handled |= USB_INTS_TRANS_COMPLETE_BITS; + if (status & USB_INTS_TRANS_COMPLETE_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; - TU_LOG(2, "Transfer complete\r\n"); hw_trans_complete(); } - if ( status & USB_INTS_ERROR_RX_TIMEOUT_BITS ) - { - handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS; + if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; } - if ( status & USB_INTS_ERROR_DATA_SEQ_BITS ) - { + if (status & USB_INTS_ERROR_DATA_SEQ_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hwbuf_ctrl_reg_host(&epx)), tu_u32_high16(*hwbuf_ctrl_reg_host(&epx))); panic("Data Seq Error \n"); } - - if ( status ^ handled ) - { - panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); - } } void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { @@ -234,54 +217,16 @@ void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { hcd_rp2040_irq(); } -static struct hw_endpoint *_next_free_interrupt_ep(void) -{ - struct hw_endpoint * ep = NULL; - for ( uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ ) - { - ep = &ep_pool[i]; - if ( !ep->configured ) - { - // Will be configured by hw_endpoint_init / hw_endpoint_allocate - ep->interrupt_num = (uint8_t) (i - 1); - return ep; - } - } - return ep; -} - -static hw_endpoint_t *hw_endpoint_allocate(uint8_t transfer_type) { - hw_endpoint_t *ep = NULL; - - if (transfer_type == TUSB_XFER_CONTROL) { - ep = &epx; - ep->hw_data_buf = &usbh_dpram->epx_data[0]; - } else { - // Note: even though datasheet name these "Interrupt" endpoints. These are actually - // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation) - ep = _next_free_interrupt_ep(); - pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num); - assert(ep); - // 0 for epx (double buffered): TODO increase to 1024 for ISO - // 2x64 for intep0 - // 3x64 for intep1 - // etc - ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)]; - } - - return ep; -} - -static void hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, - uint8_t transfer_type, uint8_t bmInterval) { - // Already has data buffer, endpoint control, and buffer control allocated at this point - assert(ep->hw_data_buf); - - uint8_t const num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - - ep->ep_addr = ep_addr; - ep->dev_addr = dev_addr; +static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { + const uint8_t ep_addr = ep_desc->bEndpointAddress; + const uint16_t wMaxPacketSize = tu_edpt_packet_size(ep_desc); + const uint8_t transfer_type = ep_desc->bmAttributes.xfer; + const uint8_t bmInterval = ep_desc->bInterval; + const uint8_t num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + ep->ep_addr = ep_addr; + ep->dev_addr = dev_addr; + ep->transfer_type = transfer_type; // Response to a setup packet on EP0 starts with pid of 1 ep->next_pid = (num == 0 ? 1u : 0u); @@ -303,7 +248,6 @@ static void hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t e io_rw_32 *ctrl_reg = hwep_ctrl_reg_host(ep); *ctrl_reg = ctrl_value; pico_trace("endpoint control (0x%p) <- 0x%lx\n", ctrl_reg, ctrl_value); - ep->configured = true; if (ep != &epx) { // Endpoint has its own addr_endp and interrupt bits to be setup! @@ -376,24 +320,17 @@ bool hcd_deinit(uint8_t rhport) { return true; } -void hcd_port_reset(uint8_t rhport) -{ +void hcd_port_reset(uint8_t rhport) { (void) rhport; - pico_trace("hcd_port_reset\n"); - assert(rhport == 0); // TODO: Nothing to do here yet. Perhaps need to reset some state? } -void hcd_port_reset_end(uint8_t rhport) -{ - (void) rhport; +void hcd_port_reset_end(uint8_t rhport) { + (void)rhport; } -bool hcd_port_connect_status(uint8_t rhport) -{ +bool hcd_port_connect_status(uint8_t rhport) { (void) rhport; - pico_trace("hcd_port_connect_status\n"); - assert(rhport == 0); return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS; } @@ -421,8 +358,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void) rhport; // reset epx if it is currently active with unplugged device - if (epx.configured && epx.active && epx.dev_addr == dev_addr) { - epx.configured = false; + if (epx.wMaxPacketSize > 0 && epx.active && epx.dev_addr == dev_addr) { + epx.wMaxPacketSize = 0; *hwep_ctrl_reg_host(&epx) = 0; *hwbuf_ctrl_reg_host(&epx) = 0; hw_endpoint_reset_transfer(&epx); @@ -432,13 +369,13 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { if (dev_addr != 0) { for (size_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { hw_endpoint_t *ep = &ep_pool[i]; - if (ep->dev_addr == dev_addr && ep->configured) { + if (ep->dev_addr == dev_addr && ep->wMaxPacketSize > 0) { // in case it is an interrupt endpoint, disable it usb_hw_clear->int_ep_ctrl = (1 << (ep->interrupt_num + 1)); usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = 0; // unconfigure the endpoint - ep->configured = false; + ep->wMaxPacketSize = 0; *hwep_ctrl_reg_host(ep) = 0; *hwbuf_ctrl_reg_host(ep) = 0; hw_endpoint_reset_transfer(ep); @@ -468,36 +405,82 @@ void hcd_int_disable(uint8_t rhport) { //--------------------------------------------------------------------+ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { (void)rhport; - pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - hw_endpoint_t *ep = hw_endpoint_allocate(ep_desc->bmAttributes.xfer); + hw_endpoint_t *ep = edpt_alloc(); TU_ASSERT(ep); - hw_endpoint_init(ep, dev_addr, ep_desc->bEndpointAddress, tu_edpt_packet_size(ep_desc), ep_desc->bmAttributes.xfer, - ep_desc->bInterval); + hw_endpoint_init(ep, dev_addr, ep_desc); return true; } bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { - (void) rhport; (void) daddr; (void) ep_addr; + (void)rhport; + (void)daddr; + (void)ep_addr; return false; // TODO not implemented yet } -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { - (void) rhport; +// xfer using epx +static bool edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen); + ep->remaining_len = total_len; + ep->xferred_len = 0; + ep->active = true; - const uint8_t ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); + if (ff != NULL) { + ep->user_fifo = ff; + ep->is_xfer_fifo = true; + } else { + ep->user_buf = buffer; + ep->is_xfer_fifo = false; + } + + ep_active = ep; - // Get appropriate ep. Either EPX or interrupt endpoint - struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr); + ep->hw_data_buf = &usbh_dpram->epx_data[0]; + uint dpram_offset = hw_data_offset(ep->hw_data_buf); + // Fill in endpoint control register with buffer offset + uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + usbh_dpram->epx_ctrl = ctrl_value; + + hw_endpoint_start_next_buffer(ep); + + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); + uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | + (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (need_pre(ep->dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + + // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit + // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". + // We write everything except the START_TRANS bit first, then wait some cycles. + usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS; + busy_wait_at_least_cycles(12); + usb_hw->sie_ctrl = flags; + + return true; +} + +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { + (void)rhport; + + hw_endpoint_t *ep = edpt_find(dev_addr, ep_addr); TU_ASSERT(ep); + // Control endpoint can change direction 0x00 <-> 0x80 + if (tu_edpt_number(ep_addr) == 0) { + ep->ep_addr = ep_addr; + ep->next_pid = 1; // data and status stage start with DATA1 + } + + edpt_xfer(ep, buffer, NULL, buflen); + + #if 0 // EP should be inactive - assert(!ep->active); + // assert(!ep->active); // Control endpoint can change direction 0x00 <-> 0x80 if (ep_addr != ep->ep_addr) { @@ -529,6 +512,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b } else { hw_endpoint_xfer_start(ep, buffer, NULL, buflen); } + #endif return true; } @@ -541,9 +525,8 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { return false; } -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) -{ - (void) rhport; +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { + (void)rhport; // Copy data into setup packet buffer for (uint8_t i = 0; i < 8; i++) { @@ -551,18 +534,15 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet } // Configure EP0 struct with setup info for the trans complete - hw_endpoint_t *ep = hw_endpoint_allocate((uint8_t)TUSB_XFER_CONTROL); + // hw_endpoint_t *ep = hw_endpoint_allocate((uint8_t)TUSB_XFER_CONTROL); + hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); - // EPX should be inactive - assert(!ep->active); - - // EP0 out - hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0); - assert(ep->configured); - + ep->ep_addr = 0; // setup is OUT ep->remaining_len = 8; - ep->active = true; + ep->active = true; + + ep_active = ep; // Set device address usb_hw->dev_addr_ctrl = dev_addr; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index c03dc34b2..682e9dab4 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -73,12 +73,13 @@ typedef struct hw_endpoint { #endif #if CFG_TUH_ENABLED - bool configured; // Is this a valid struct uint8_t dev_addr; uint8_t interrupt_num; // for host interrupt endpoints + uint8_t transfer_type; + bool need_pre; // need preamble for low speed device behind full speed hub #endif - uint16_t wMaxPacketSize; + uint16_t wMaxPacketSize; // max packet size also indicates configured uint8_t *hw_data_buf; // Buffer pointer in usb dpram // transfer info -- cgit v1.3.1 From 084ef43be65484c3fd928d1f224aed70b3d6af8a Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 16 Jan 2026 11:48:09 +0700 Subject: refactor hcd, get both control and interrupt endpoint working --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 33 ++- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 364 +++++++++++++++------------ src/portable/raspberrypi/rp2040/rp2040_usb.c | 84 +++---- src/portable/raspberrypi/rp2040/rp2040_usb.h | 65 ++--- 4 files changed, 290 insertions(+), 256 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 240e6c727..8c7dc83b8 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -69,6 +69,22 @@ TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8 return hw_endpoint_get(num, dir); } +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) { + const uint8_t epnum = tu_edpt_number(ep->ep_addr); + const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); + if (epnum == 0) { + // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled + return NULL; + } + return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out; +} + +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) { + const uint8_t epnum = tu_edpt_number(ep->ep_addr); + const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); + return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out; +} + // main processing for dcd_edpt_iso_activate static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { ep->ep_addr = ep_addr; @@ -170,7 +186,10 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - const bool done = hw_endpoint_xfer_continue(ep); + io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); + io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg); + if (done) { // Notify usbd const uint16_t xferred_len = ep->xferred_len; @@ -232,7 +251,9 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { hw_endpoint_lock_update(ep, 1); if (ep->pending) { ep->pending = 0; - hw_endpoint_start_next_buffer(ep); + io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); + io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); } hw_endpoint_lock_update(ep, -1); } @@ -501,7 +522,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to (void)rhport; (void)is_isr; hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_xfer_start(ep, buffer, NULL, total_bytes); + io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); + io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, total_bytes); return true; } @@ -509,7 +532,9 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t (void)rhport; (void)is_isr; hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_xfer_start(ep, NULL, ff, total_bytes); + io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); + io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + hw_endpoint_xfer_start(ep, ep_reg, buf_reg, NULL, ff, total_bytes); return true; } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 8e3379622..ff2db2499 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -47,42 +47,42 @@ // Low level rp2040 controller functions //--------------------------------------------------------------------+ -#ifndef PICO_USB_HOST_INTERRUPT_ENDPOINTS -#define PICO_USB_HOST_INTERRUPT_ENDPOINTS (USB_MAX_ENDPOINTS - 1) -#endif -static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, ""); - // Host mode uses one shared endpoint register for non-interrupt endpoint -static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS]; -#define epx (ep_pool[0]) - -static hw_endpoint_t *ep_active = NULL; +static hcd_endpoint_t ep_pool[USB_MAX_ENDPOINTS]; +static hcd_endpoint_t *epx = &ep_pool[0]; // current active endpoint // Flags we set by default in sie_ctrl (we add other bits on top) enum { - SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | - USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS + SIE_CTRL_BASE = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS, + SIE_CTRL_BASE_MASK = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS | USB_SIE_CTRL_SOF_EN_BITS | + USB_SIE_CTRL_KEEP_ALIVE_EN_BITS +}; + +enum { + SIE_CTRL_SPEED_DISCONNECT = 0, + SIE_CTRL_SPEED_LOW = 1, + SIE_CTRL_SPEED_FULL = 2, }; //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -static hw_endpoint_t *edpt_alloc(void) { +static hcd_endpoint_t *edpt_alloc(void) { for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hw_endpoint_t *ep = &ep_pool[i]; - if (ep->wMaxPacketSize == 0) { + hcd_endpoint_t *ep = &ep_pool[i]; + if (ep->hwep.wMaxPacketSize == 0) { return ep; } } return NULL; } -static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { +static hcd_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { for (uint32_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - struct hw_endpoint *ep = &ep_pool[i]; - if ((ep->dev_addr == daddr) && (ep->wMaxPacketSize > 0) && - (ep->ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->ep_addr) == 0))) { + hcd_endpoint_t *ep = &ep_pool[i]; + if ((ep->dev_addr == daddr) && (ep->hwep.wMaxPacketSize > 0) && + (ep->hwep.ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->hwep.ep_addr) == 0))) { return ep; } } @@ -90,6 +90,24 @@ static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { return NULL; } +// static hcd_endpoint_t* epdt_find_interrupt(uint8_t ) + +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(hw_endpoint_t *ep) { + if (tu_edpt_number(ep->ep_addr) == 0) { + return &usbh_dpram->epx_ctrl; + } + // return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; + return NULL; +} + +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_host(hw_endpoint_t *ep) { + if (tu_edpt_number(ep->ep_addr) == 0) { + return &usbh_dpram->epx_buf_ctrl; + } + // return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; + return NULL; +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -104,18 +122,17 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } -static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result) { +static void __tusb_irq_path_func(hw_xfer_complete)(hcd_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack - uint8_t dev_addr = ep->dev_addr; - uint8_t ep_addr = ep->ep_addr; - uint xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); + uint8_t dev_addr = ep->dev_addr; + uint8_t ep_addr = ep->hwep.ep_addr; + uint xferred_len = ep->hwep.xferred_len; + hw_endpoint_reset_transfer(&ep->hwep); hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } -static void __tusb_irq_path_func(handle_hwbuf_status_bit)(uint bit, struct hw_endpoint *ep) { - usb_hw_clear->buf_status = bit; - const bool done = hw_endpoint_xfer_continue(ep); +static void __tusb_irq_path_func(handle_hwbuf_status_bit)(hcd_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { + const bool done = hw_endpoint_xfer_continue(&ep->hwep, ep_reg, buf_reg); if (done) { hw_xfer_complete(ep, XFER_RESULT_SUCCESS); } @@ -129,25 +146,35 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { uint32_t bit = 1u; if (buf_status & bit) { buf_status &= ~bit; - hw_endpoint_t *ep = ep_active; - handle_hwbuf_status_bit(bit, ep); + usb_hw_clear->buf_status = bit; + + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + handle_hwbuf_status_bit(epx, ep_reg, buf_reg); } // Check "interrupt" (asynchronous) endpoints for both IN and OUT + // TODO use clz for better efficiency for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && buf_status; i++) { - // EPX is bit 0 & 1 - // IEP1 IN is bit 2 - // IEP1 OUT is bit 3 - // IEP2 IN is bit 4 - // IEP2 OUT is bit 5 - // IEP3 IN is bit 6 - // IEP3 OUT is bit 7 + // EPX IN/OUT is bit 0, 1 + // IEP1 IN/OUT is bit 2, 3 + // IEP2 IN/OUT is bit 4, 5 // etc for (uint j = 0; j < 2; j++) { bit = 1 << (i * 2 + j); if (buf_status & bit) { buf_status &= ~bit; - handle_hwbuf_status_bit(bit, &ep_pool[i]); + usb_hw_clear->buf_status = bit; + + for (uint8_t e = 0; e < USB_MAX_ENDPOINTS; e++) { + hcd_endpoint_t *ep = &ep_pool[e]; + if (ep->interrupt_num == i) { + io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; + io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; + handle_hwbuf_status_bit(ep, ep_reg, buf_reg); + break; + } + } } } } @@ -157,27 +184,24 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } } -static void __tusb_irq_path_func(hw_trans_complete)(void) { - if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { - hw_endpoint_t *ep = ep_active; - ep->xferred_len = 8; - hw_xfer_complete(ep, XFER_RESULT_SUCCESS); - } else { - // Don't care. Will handle this in buff status - return; - } -} +// static void edpt_scheduler(void) { +// } static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { const uint32_t status = usb_hw->ints; if (status & USB_INTS_HOST_CONN_DIS_BITS) { - if (dev_speed()) { - hcd_event_device_attach(RHPORT_NATIVE, true); - } else { + uint8_t speed = dev_speed(); + if (speed == SIE_CTRL_SPEED_DISCONNECT) { hcd_event_device_remove(RHPORT_NATIVE, true); + } else { + if (speed == SIE_CTRL_SPEED_LOW) { + usb_hw->sie_ctrl = SIE_CTRL_BASE | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS; + } else { + usb_hw->sie_ctrl = SIE_CTRL_BASE | USB_SIE_CTRL_SOF_EN_BITS; + } + hcd_event_device_attach(RHPORT_NATIVE, true); } - usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS; } @@ -187,7 +211,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // AND TRANS_COMPLETE as the stall is an alternative response // to one of those events usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; - hw_xfer_complete(&epx, XFER_RESULT_STALLED); + hw_xfer_complete(epx, XFER_RESULT_STALLED); } if (status & USB_INTS_BUFF_STATUS_BITS) { @@ -196,7 +220,14 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { if (status & USB_INTS_TRANS_COMPLETE_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; - hw_trans_complete(); + + // only handle setup packet + if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { + epx->hwep.xferred_len = 8; + hw_xfer_complete(epx, XFER_RESULT_SUCCESS); + } else { + // Don't care. Will handle this in buff status + } } if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { @@ -205,36 +236,69 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { if (status & USB_INTS_ERROR_DATA_SEQ_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; - TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*hwbuf_ctrl_reg_host(&epx)), - tu_u32_high16(*hwbuf_ctrl_reg_host(&epx))); panic("Data Seq Error \n"); } } void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { - (void) rhport; - (void) in_isr; + (void)rhport; + (void)in_isr; hcd_rp2040_irq(); } -static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { +static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { const uint8_t ep_addr = ep_desc->bEndpointAddress; const uint16_t wMaxPacketSize = tu_edpt_packet_size(ep_desc); const uint8_t transfer_type = ep_desc->bmAttributes.xfer; - const uint8_t bmInterval = ep_desc->bInterval; - const uint8_t num = tu_edpt_number(ep_addr); - const tusb_dir_t dir = tu_edpt_dir(ep_addr); - ep->ep_addr = ep_addr; - ep->dev_addr = dev_addr; - ep->transfer_type = transfer_type; - - // Response to a setup packet on EP0 starts with pid of 1 - ep->next_pid = (num == 0 ? 1u : 0u); - ep->wMaxPacketSize = wMaxPacketSize; - - pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, transfer_type); - pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf); - uint dpram_offset = hw_data_offset(ep->hw_data_buf); + // const uint8_t bmInterval = ep_desc->bInterval; + + ep->hwep.ep_addr = ep_addr; + ep->dev_addr = dev_addr; + ep->transfer_type = transfer_type; + ep->need_pre = need_pre(dev_addr); + ep->hwep.next_pid = 0u; + ep->hwep.wMaxPacketSize = wMaxPacketSize; + + if (transfer_type != TUSB_XFER_INTERRUPT) { + ep->hwep.hw_data_buf = usbh_dpram->epx_data; + } else { + // from 15 interrupt endpoints pool + uint8_t int_idx; + for (int_idx = 0; int_idx < USB_HOST_INTERRUPT_ENDPOINTS; int_idx++) { + if (!tu_bit_test(usb_hw_set->int_ep_ctrl, 1 + int_idx)) { + ep->interrupt_num = int_idx + 1; + break; + } + } + assert(int_idx < USB_HOST_INTERRUPT_ENDPOINTS); + assert(ep_desc->bInterval > 0); + + //------------- dpram buf -------------// + // 15x64 last bytes of DPRAM for interrupt endpoint buffers + ep->hwep.hw_data_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); + uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hwep.hw_data_buf) | + (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; + + //------------- address control -------------// + const uint8_t epnum = tu_edpt_number(ep_addr); + uint32_t addr_ctrl = (uint32_t)(dev_addr | (epnum << USB_ADDR_ENDP1_ENDPOINT_LSB)); + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + addr_ctrl |= USB_ADDR_ENDP1_INTEP_DIR_BITS; + } + if (ep->need_pre) { + addr_ctrl |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; + } + usb_hw->int_ep_addr_ctrl[int_idx] = addr_ctrl; + + // Finally, activate interrupt endpoint + usb_hw_set->int_ep_ctrl |= 1u << ep->interrupt_num; + } + #if 0 + pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->hwep.ep_addr, transfer_type); + pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->hwep.ep_addr, ep->hwep.hw_data_buf); + uint dpram_offset = hw_data_offset(ep->hwep.hw_data_buf); // Bits 0-5 should be 0 assert(!(dpram_offset & 0b111111)); @@ -271,6 +335,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_des // If it's an interrupt endpoint we need to set up the buffer control register } + #endif } //--------------------------------------------------------------------+ @@ -312,11 +377,9 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { bool hcd_deinit(uint8_t rhport) { (void) rhport; - irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq); reset_block(RESETS_RESET_USBCTRL_BITS); unreset_block_wait(RESETS_RESET_USBCTRL_BITS); - return true; } @@ -330,58 +393,54 @@ void hcd_port_reset_end(uint8_t rhport) { } bool hcd_port_connect_status(uint8_t rhport) { - (void) rhport; + (void)rhport; return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS; } -tusb_speed_t hcd_port_speed_get(uint8_t rhport) -{ - (void) rhport; - assert(rhport == 0); - - // TODO: Should enumval this register - switch ( dev_speed() ) - { - case 1: +tusb_speed_t hcd_port_speed_get(uint8_t rhport) { + (void)rhport; + switch (dev_speed()) { + case SIE_CTRL_SPEED_LOW: return TUSB_SPEED_LOW; - case 2: + case SIE_CTRL_SPEED_FULL: return TUSB_SPEED_FULL; default: - panic("Invalid speed\n"); - // return TUSB_SPEED_INVALID; + return TUSB_SPEED_INVALID; } } // Close all opened endpoint belong to this device void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { - pico_trace("hcd_device_close %d\n", dev_addr); - (void) rhport; + (void)rhport; + (void)dev_addr; + #if 0 // reset epx if it is currently active with unplugged device - if (epx.wMaxPacketSize > 0 && epx.active && epx.dev_addr == dev_addr) { - epx.wMaxPacketSize = 0; + if (epx.hw_ep.wMaxPacketSize > 0 && epx.hw_ep.active && epx.dev_addr == dev_addr) { + epx.hw_ep.wMaxPacketSize = 0; *hwep_ctrl_reg_host(&epx) = 0; *hwbuf_ctrl_reg_host(&epx) = 0; - hw_endpoint_reset_transfer(&epx); + hw_endpoint_reset_transfer(&epx.hw_ep); } // dev0 only has ep0 if (dev_addr != 0) { for (size_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { - hw_endpoint_t *ep = &ep_pool[i]; - if (ep->dev_addr == dev_addr && ep->wMaxPacketSize > 0) { + hcd_endpoint_t *ep = &ep_pool[i]; + if (ep->dev_addr == dev_addr && ep->hwep.wMaxPacketSize > 0) { // in case it is an interrupt endpoint, disable it usb_hw_clear->int_ep_ctrl = (1 << (ep->interrupt_num + 1)); usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = 0; // unconfigure the endpoint - ep->wMaxPacketSize = 0; + ep->hwep.wMaxPacketSize = 0; *hwep_ctrl_reg_host(ep) = 0; *hwbuf_ctrl_reg_host(ep) = 0; - hw_endpoint_reset_transfer(ep); + hw_endpoint_reset_transfer(&ep->hwep); } } } + #endif } uint32_t hcd_frame_number(uint8_t rhport) { @@ -405,9 +464,9 @@ void hcd_int_disable(uint8_t rhport) { //--------------------------------------------------------------------+ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { (void)rhport; - hw_endpoint_t *ep = edpt_alloc(); + pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); + hcd_endpoint_t *ep = edpt_alloc(); TU_ASSERT(ep); - hw_endpoint_init(ep, dev_addr, ep_desc); return true; @@ -420,46 +479,49 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } -// xfer using epx -static bool edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); +TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { + value |= (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK); // preserve base bits - ep->remaining_len = total_len; - ep->xferred_len = 0; - ep->active = true; + // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit + // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". + // We write everything except the START_TRANS bit first, then wait some cycles. + usb_hw->sie_ctrl = value; + busy_wait_at_least_cycles(12); + usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; +} - if (ff != NULL) { - ep->user_fifo = ff; - ep->is_xfer_fifo = true; +// xfer using epx +static bool edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { + if (ep->transfer_type == TUSB_XFER_INTERRUPT) { + // For interrupt endpoint control and buffer is already configured + // Note: Interrupt is single buffered only + io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; + io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; + hw_endpoint_xfer_start(&ep->hwep, ep_reg, buf_reg, buffer, ff, total_len); } else { - ep->user_buf = buffer; - ep->is_xfer_fifo = false; - } - - ep_active = ep; + const uint8_t ep_num = tu_edpt_number(ep->hwep.ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->hwep.ep_addr); - ep->hw_data_buf = &usbh_dpram->epx_data[0]; - uint dpram_offset = hw_data_offset(ep->hw_data_buf); + // ep control + const uint32_t dpram_offset = hw_data_offset(ep->hwep.hw_data_buf); + const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + usbh_dpram->epx_ctrl = ep_ctrl; - // Fill in endpoint control register with buffer offset - uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; - usbh_dpram->epx_ctrl = ctrl_value; + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + hw_endpoint_xfer_start(&ep->hwep, ep_reg, buf_reg, buffer, ff, total_len); - hw_endpoint_start_next_buffer(ep); + // addr control + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | - (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (need_pre(ep->dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + epx = ep; - // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit - // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". - // We write everything except the START_TRANS bit first, then wait some cycles. - usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS; - busy_wait_at_least_cycles(12); - usb_hw->sie_ctrl = flags; + // start transfer + const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); + } return true; } @@ -467,13 +529,13 @@ static bool edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void)rhport; - hw_endpoint_t *ep = edpt_find(dev_addr, ep_addr); + hcd_endpoint_t *ep = edpt_find(dev_addr, ep_addr); TU_ASSERT(ep); // Control endpoint can change direction 0x00 <-> 0x80 - if (tu_edpt_number(ep_addr) == 0) { - ep->ep_addr = ep_addr; - ep->next_pid = 1; // data and status stage start with DATA1 + if (ep_addr != ep->hwep.ep_addr) { + ep->hwep.ep_addr = ep_addr; + ep->hwep.next_pid = 1; // data and status stage start with DATA1 } edpt_xfer(ep, buffer, NULL, buflen); @@ -518,9 +580,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b } bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; + (void)rhport; + (void)dev_addr; + (void)ep_addr; // TODO not implemented yet return false; } @@ -533,38 +595,30 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet usbh_dpram->setup_packet[i] = setup_packet[i]; } - // Configure EP0 struct with setup info for the trans complete - // hw_endpoint_t *ep = hw_endpoint_allocate((uint8_t)TUSB_XFER_CONTROL); - hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); + hcd_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); - ep->ep_addr = 0; // setup is OUT - ep->remaining_len = 8; - ep->active = true; + ep->hwep.ep_addr = 0; // setup is OUT + ep->hwep.remaining_len = 8; + ep->hwep.xferred_len = 0; + ep->hwep.active = true; - ep_active = ep; + epx = ep; // Set device address usb_hw->dev_addr_ctrl = dev_addr; // Set pre if we are a low speed device on full speed hub - uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS | - (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - - // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit - // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". - // We write everything except the START_TRANS bit first, then wait some cycles. - usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS; - busy_wait_at_least_cycles(12); - usb_hw->sie_ctrl = flags; + const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); return true; } bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; + (void)rhport; + (void)dev_addr; + (void)ep_addr; panic("hcd_clear_stall"); // return true; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 3b65f57a4..74c6bf655 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -35,7 +35,7 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ -static void sync_xfer(hw_endpoint_t *ep); +static void sync_xfer(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX static bool e15_is_critical_frame_period(struct hw_endpoint *ep); @@ -92,6 +92,7 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->remaining_len = 0; ep->xferred_len = 0; ep->user_buf = 0; + ep->is_xfer_fifo = false; } void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) { @@ -124,7 +125,7 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an } // prepare buffer, move data if tx, return buffer control -static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { +uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); @@ -166,33 +167,23 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, } // Prepare buffer control register value -void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { +void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - bool is_rx; - bool is_host = false; - io_rw_32 *ep_ctrl_reg; - io_rw_32 *buf_ctrl_reg; + const bool is_host = rp2usb_is_host_mode(); - #if CFG_TUH_ENABLED - is_host = rp2usb_is_host_mode(); + bool is_rx; if (is_host) { - buf_ctrl_reg = hwbuf_ctrl_reg_host(ep); - ep_ctrl_reg = hwep_ctrl_reg_host(ep); - is_rx = (dir == TUSB_DIR_IN); - } else - #endif - { - buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); - ep_ctrl_reg = hwep_ctrl_reg_device(ep); - is_rx = (dir == TUSB_DIR_OUT); + is_rx = (dir == TUSB_DIR_IN); + } else { + is_rx = (dir == TUSB_DIR_OUT); } // always compute and start with buffer 0 - uint32_t buf_ctrl = prepare_ep_buffer(ep, 0, is_rx) | USB_BUF_CTRL_SEL; + uint32_t buf_ctrl = hwbuf_prepare(ep, 0, is_rx) | USB_BUF_CTRL_SEL; // EP0 has no endpoint control register, also usbd only schedule 1 packet at a time (single buffer) - if (ep_ctrl_reg != NULL) { - uint32_t ep_ctrl = *ep_ctrl_reg; + if (ep_reg != NULL) { + uint32_t ep_ctrl = *ep_reg; // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause // short packet on buffer0 @@ -205,7 +196,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) // Use buffer 1 (double buffered) if there is still data // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) - buf_ctrl |= prepare_ep_buffer(ep, 1, is_rx); + buf_ctrl |= hwbuf_prepare(ep, 1, is_rx); // Set endpoint control double buffered bit if needed ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; @@ -216,17 +207,18 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; } - *ep_ctrl_reg = ep_ctrl; + *ep_reg = ep_ctrl; } TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); // Finally, write to buffer_control which will trigger the transfer // the next time the controller polls this dpram address - hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); + hwbuf_ctrl_set(buf_reg, buf_ctrl); } -void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { +void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, + uint16_t total_len) { hw_endpoint_lock_update(ep, 1); if (ep->active) { @@ -258,15 +250,14 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t * } else #endif { - hw_endpoint_start_next_buffer(ep); + hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); } hw_endpoint_lock_update(ep, -1); } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, - bool is_rx) { +uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, bool is_rx) { uint32_t buf_ctrl = *buf_ctrl_reg; if (buf_id) { buf_ctrl = buf_ctrl >> 16; @@ -304,37 +295,26 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 } // Update hw endpoint struct with info from hardware after a buff status interrupt -static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep) { +static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { // const uint8_t ep_num = tu_edpt_number(ep->ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); + const bool is_host = rp2usb_is_host_mode(); + bool is_rx; - io_rw_32 *buf_ctrl_reg; - io_rw_32 *ep_ctrl_reg; - bool is_rx; - - #if CFG_TUH_ENABLED - const bool is_host = rp2usb_is_host_mode(); if (is_host) { - buf_ctrl_reg = hwbuf_ctrl_reg_host(ep); - ep_ctrl_reg = hwep_ctrl_reg_host(ep); - is_rx = (dir == TUSB_DIR_IN); - } else - #endif - { - buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); - ep_ctrl_reg = hwep_ctrl_reg_device(ep); - is_rx = (dir == TUSB_DIR_OUT); + is_rx = (dir == TUSB_DIR_IN); + } else { + is_rx = (dir == TUSB_DIR_OUT); } - TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_ctrl_reg), - tu_u32_high16(*buf_ctrl_reg)); - uint16_t buf0_bytes = sync_ep_buffer(ep, buf_ctrl_reg, 0, is_rx); // always sync buffer 0 + TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_reg), tu_u32_high16(*buf_reg)); + uint16_t buf0_bytes = hwbuf_sync(ep, buf_reg, 0, is_rx); // always sync buffer 0 // sync buffer 1 if double buffered - if (ep_ctrl_reg != NULL && (*ep_ctrl_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) { + if (ep_reg != NULL && (*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) { if (buf0_bytes == ep->wMaxPacketSize) { // sync buffer 1 if not short packet - sync_ep_buffer(ep, buf_ctrl_reg, 1, is_rx); + hwbuf_sync(ep, buf_reg, 1, is_rx); } else { // short packet on buffer 0 // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example @@ -368,7 +348,7 @@ static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep) { } // Returns true if transfer is complete -bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { +bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { hw_endpoint_lock_update(ep, 1); // Part way through a transfer @@ -376,7 +356,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); } - sync_xfer(ep); // Update EP struct from hardware state + sync_xfer(ep, ep_reg, buf_reg); // Update EP struct from hardware state // Now we have synced our state with the hardware. Is there more data to transfer? // If we are done then notify tinyusb @@ -392,7 +372,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { } else #endif { - hw_endpoint_start_next_buffer(ep); + hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); } } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 682e9dab4..ac1d8610a 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -72,13 +72,6 @@ typedef struct hw_endpoint { uint8_t pending; // Transfer scheduled but not active #endif -#if CFG_TUH_ENABLED - uint8_t dev_addr; - uint8_t interrupt_num; // for host interrupt endpoints - uint8_t transfer_type; - bool need_pre; // need preamble for low speed device behind full speed hub -#endif - uint16_t wMaxPacketSize; // max packet size also indicates configured uint8_t *hw_data_buf; // Buffer pointer in usb dpram @@ -92,6 +85,15 @@ typedef struct hw_endpoint { } hw_endpoint_t; +// Host controller endpoint +typedef struct { + hw_endpoint_t hwep; + uint8_t dev_addr; + uint8_t interrupt_num; // 1-15 for interrupt endpoints + uint8_t transfer_type; + bool need_pre; // need preamble for low speed device behind full speed hub +} hcd_endpoint_t; + #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX extern volatile uint32_t e15_last_sof; #endif @@ -103,10 +105,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; } -void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); -bool hw_endpoint_xfer_continue(struct hw_endpoint *ep); +//--------------------------------------------------------------------+ +// Hardware Endpoint +//--------------------------------------------------------------------+ +void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, + uint16_t total_len); +bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); void hw_endpoint_reset_transfer(struct hw_endpoint *ep); -void hw_endpoint_start_next_buffer(struct hw_endpoint *ep); +void hw_endpoint_start_next_buffer(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { // todo add critsec as necessary to prevent issues between worker and IRQ... @@ -114,43 +120,12 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. } -// #if CFG_TUD_ENABLED -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) { - uint8_t const epnum = tu_edpt_number(ep->ep_addr); - const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); - if (epnum == 0) { - // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled - return NULL; - } - return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out; -} - -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) { - const uint8_t epnum = tu_edpt_number(ep->ep_addr); - const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); - return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out; -} -// #endif - -#if CFG_TUH_ENABLED -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(struct hw_endpoint *ep) { - if (tu_edpt_number(ep->ep_addr) == 0) { - return &usbh_dpram->epx_ctrl; - } - return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; -} - -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_host(struct hw_endpoint *ep) { - if (tu_edpt_number(ep->ep_addr) == 0) { - return &usbh_dpram->epx_buf_ctrl; - } - return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; -} -#endif - //--------------------------------------------------------------------+ -// +// Hardware Buffer //--------------------------------------------------------------------+ +uint32_t hwbuf_prepare(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx); +uint16_t hwbuf_sync(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, bool is_rx); + void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { -- cgit v1.3.1 From 0c9a170dca9b0a0982b4b0462508ee4bda7a06da Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 16 Jan 2026 13:39:06 +0700 Subject: implement hcd_device_close() --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 123 +++++---------------------- 1 file changed, 20 insertions(+), 103 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index ff2db2499..8a891c49a 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -252,12 +252,12 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de const uint8_t transfer_type = ep_desc->bmAttributes.xfer; // const uint8_t bmInterval = ep_desc->bInterval; + ep->hwep.wMaxPacketSize = wMaxPacketSize; ep->hwep.ep_addr = ep_addr; ep->dev_addr = dev_addr; ep->transfer_type = transfer_type; ep->need_pre = need_pre(dev_addr); ep->hwep.next_pid = 0u; - ep->hwep.wMaxPacketSize = wMaxPacketSize; if (transfer_type != TUSB_XFER_INTERRUPT) { ep->hwep.hw_data_buf = usbh_dpram->epx_data; @@ -293,49 +293,8 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de usb_hw->int_ep_addr_ctrl[int_idx] = addr_ctrl; // Finally, activate interrupt endpoint - usb_hw_set->int_ep_ctrl |= 1u << ep->interrupt_num; - } - #if 0 - pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->hwep.ep_addr, transfer_type); - pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->hwep.ep_addr, ep->hwep.hw_data_buf); - uint dpram_offset = hw_data_offset(ep->hwep.hw_data_buf); - // Bits 0-5 should be 0 - assert(!(dpram_offset & 0b111111)); - - // Fill in endpoint control register with buffer offset - uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; - if (bmInterval) { - ctrl_value |= (uint32_t)((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); - } - - io_rw_32 *ctrl_reg = hwep_ctrl_reg_host(ep); - *ctrl_reg = ctrl_value; - pico_trace("endpoint control (0x%p) <- 0x%lx\n", ctrl_reg, ctrl_value); - - if (ep != &epx) { - // Endpoint has its own addr_endp and interrupt bits to be setup! - // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with: - // - device address - // - endpoint number / direction - // - preamble - uint32_t reg = (uint32_t)(dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB)); - - if (dir == TUSB_DIR_OUT) { - reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS; - } - - if (need_pre(dev_addr)) { - reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; - } - usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg; - - // Finally, enable interrupt that endpoint - usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1); - - // If it's an interrupt endpoint we need to set up the buffer control register + usb_hw_set->int_ep_ctrl = 1u << ep->interrupt_num; } - #endif } //--------------------------------------------------------------------+ @@ -414,33 +373,29 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)rhport; (void)dev_addr; - #if 0 // reset epx if it is currently active with unplugged device - if (epx.hw_ep.wMaxPacketSize > 0 && epx.hw_ep.active && epx.dev_addr == dev_addr) { - epx.hw_ep.wMaxPacketSize = 0; - *hwep_ctrl_reg_host(&epx) = 0; - *hwbuf_ctrl_reg_host(&epx) = 0; - hw_endpoint_reset_transfer(&epx.hw_ep); + if (epx->hwep.wMaxPacketSize > 0 && epx->dev_addr == dev_addr) { + // if (epx->hwep.active) { + // // need to abort transfer + // } + epx->hwep.wMaxPacketSize = 0; } - // dev0 only has ep0 - if (dev_addr != 0) { - for (size_t i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { - hcd_endpoint_t *ep = &ep_pool[i]; - if (ep->dev_addr == dev_addr && ep->hwep.wMaxPacketSize > 0) { - // in case it is an interrupt endpoint, disable it - usb_hw_clear->int_ep_ctrl = (1 << (ep->interrupt_num + 1)); - usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = 0; - - // unconfigure the endpoint - ep->hwep.wMaxPacketSize = 0; - *hwep_ctrl_reg_host(ep) = 0; - *hwbuf_ctrl_reg_host(ep) = 0; - hw_endpoint_reset_transfer(&ep->hwep); + for (size_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + hcd_endpoint_t *ep = &ep_pool[i]; + if (ep->dev_addr == dev_addr && ep->hwep.wMaxPacketSize > 0) { + if (ep->interrupt_num) { + // disable interrupt endpoint + usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; + usb_hw->int_ep_addr_ctrl[ep->interrupt_num - 1] = 0; + + usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl = 0; + usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl = 0; } + + ep->hwep.wMaxPacketSize = 0; // mark as unused } } - #endif } uint32_t hcd_frame_number(uint8_t rhport) { @@ -491,7 +446,7 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { } // xfer using epx -static bool edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { +static void edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { if (ep->transfer_type == TUSB_XFER_INTERRUPT) { // For interrupt endpoint control and buffer is already configured // Note: Interrupt is single buffered only @@ -522,8 +477,6 @@ static bool edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16 (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); sie_start_xfer(sie_ctrl); } - - return true; } bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { @@ -540,42 +493,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b edpt_xfer(ep, buffer, NULL, buflen); - #if 0 - // EP should be inactive - // assert(!ep->active); - - // Control endpoint can change direction 0x00 <-> 0x80 - if (ep_addr != ep->ep_addr) { - assert(ep_num == 0); - - // Direction has flipped on endpoint control so re init it but with same properties - hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, TUSB_XFER_CONTROL, 0); - } - - // If a normal transfer (non-interrupt) then initiate using - // sie ctrl registers. Otherwise, interrupt ep registers should - // already be configured - if (ep == &epx) { - hw_endpoint_xfer_start(ep, buffer, NULL, buflen); - - // That has set up buffer control, endpoint control etc - // for host we have to initiate the transfer - usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - - uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE | - (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit - // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". - // We write everything except the START_TRANS bit first, then wait some cycles. - usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS; - busy_wait_at_least_cycles(12); - usb_hw->sie_ctrl = flags; - } else { - hw_endpoint_xfer_start(ep, buffer, NULL, buflen); - } - #endif - return true; } -- cgit v1.3.1 From 292f334bd9b9d76524bbd6b76aea3cf9e6fd24c4 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 16 Jan 2026 15:03:09 +0700 Subject: rename --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 14 +++++++------- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 23 ++++++++++++----------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 15 ++++++++------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 9 +++------ 4 files changed, 30 insertions(+), 31 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 8c7dc83b8..7af6cdb43 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -89,7 +89,7 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_en static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { ep->ep_addr = ep_addr; ep->next_pid = 0u; - ep->wMaxPacketSize = wMaxPacketSize; + ep->max_packet_size = wMaxPacketSize; // Clear existing buffer control state io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); @@ -99,7 +99,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa const uint8_t epnum = tu_edpt_number(ep_addr); if (epnum == 0) { // Buffer offset is fixed (also double buffered) - ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; + ep->dpram_buf = (uint8_t *)&usb_dpram->ep0_buf_a[0]; } else { // round up size to multiple of 64 uint16_t size = (uint16_t)tu_round_up(wMaxPacketSize, 64); @@ -116,11 +116,11 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa } // assign buffer - ep->hw_data_buf = hw_buffer_ptr; + ep->dpram_buf = hw_buffer_ptr; hw_buffer_ptr += size; hard_assert(hw_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data)); - pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); + pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->dpram_buf); } } @@ -129,7 +129,7 @@ static void hw_endpoint_enable(hw_endpoint_t *ep, uint8_t transfer_type) { // Set endpoint control register to enable (EP0 has no endpoint control register) if (ctrl_reg != NULL) { const uint32_t ctrl_value = - EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf); + EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf); *ctrl_reg = ctrl_value; } } @@ -501,12 +501,12 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) const uint8_t epnum = tu_edpt_number(ep_desc->bEndpointAddress); const tusb_dir_t dir = tu_edpt_dir(ep_desc->bEndpointAddress); struct hw_endpoint *ep = hw_endpoint_get(epnum, dir); - TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and allocated previously + TU_ASSERT(ep->dpram_buf != NULL); // must be inited and allocated previously if (ep->active) { hw_endpoint_abort_xfer(ep); // abort any pending transfer } - ep->wMaxPacketSize = ep_desc->wMaxPacketSize; + ep->max_packet_size = ep_desc->wMaxPacketSize; hw_endpoint_enable(ep, TUSB_XFER_ISOCHRONOUS); return true; diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 8a891c49a..6377a5d75 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -71,7 +71,7 @@ enum { static hcd_endpoint_t *edpt_alloc(void) { for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hcd_endpoint_t *ep = &ep_pool[i]; - if (ep->hwep.wMaxPacketSize == 0) { + if (ep->hwep.max_packet_size == 0) { return ep; } } @@ -81,7 +81,7 @@ static hcd_endpoint_t *edpt_alloc(void) { static hcd_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { for (uint32_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hcd_endpoint_t *ep = &ep_pool[i]; - if ((ep->dev_addr == daddr) && (ep->hwep.wMaxPacketSize > 0) && + if ((ep->dev_addr == daddr) && (ep->hwep.max_packet_size > 0) && (ep->hwep.ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->hwep.ep_addr) == 0))) { return ep; } @@ -184,6 +184,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } } +// // static void edpt_scheduler(void) { // } @@ -252,7 +253,7 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de const uint8_t transfer_type = ep_desc->bmAttributes.xfer; // const uint8_t bmInterval = ep_desc->bInterval; - ep->hwep.wMaxPacketSize = wMaxPacketSize; + ep->hwep.max_packet_size = wMaxPacketSize; ep->hwep.ep_addr = ep_addr; ep->dev_addr = dev_addr; ep->transfer_type = transfer_type; @@ -260,7 +261,7 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de ep->hwep.next_pid = 0u; if (transfer_type != TUSB_XFER_INTERRUPT) { - ep->hwep.hw_data_buf = usbh_dpram->epx_data; + ep->hwep.dpram_buf = usbh_dpram->epx_data; } else { // from 15 interrupt endpoints pool uint8_t int_idx; @@ -275,9 +276,9 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de //------------- dpram buf -------------// // 15x64 last bytes of DPRAM for interrupt endpoint buffers - ep->hwep.hw_data_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); + ep->hwep.dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hwep.hw_data_buf) | + (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hwep.dpram_buf) | (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; @@ -374,16 +375,16 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)dev_addr; // reset epx if it is currently active with unplugged device - if (epx->hwep.wMaxPacketSize > 0 && epx->dev_addr == dev_addr) { + if (epx->hwep.max_packet_size > 0 && epx->dev_addr == dev_addr) { // if (epx->hwep.active) { // // need to abort transfer // } - epx->hwep.wMaxPacketSize = 0; + epx->hwep.max_packet_size = 0; } for (size_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hcd_endpoint_t *ep = &ep_pool[i]; - if (ep->dev_addr == dev_addr && ep->hwep.wMaxPacketSize > 0) { + if (ep->dev_addr == dev_addr && ep->hwep.max_packet_size > 0) { if (ep->interrupt_num) { // disable interrupt endpoint usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; @@ -393,7 +394,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl = 0; } - ep->hwep.wMaxPacketSize = 0; // mark as unused + ep->hwep.max_packet_size = 0; // mark as unused } } } @@ -458,7 +459,7 @@ static void edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16 const tusb_dir_t ep_dir = tu_edpt_dir(ep->hwep.ep_addr); // ep control - const uint32_t dpram_offset = hw_data_offset(ep->hwep.hw_data_buf); + const uint32_t dpram_offset = hw_data_offset(ep->hwep.dpram_buf); const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; usbh_dpram->epx_ctrl = ep_ctrl; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 74c6bf655..477e50a6a 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -125,8 +125,8 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an } // prepare buffer, move data if tx, return buffer control -uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { - const uint16_t buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); +static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { + const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; @@ -138,7 +138,7 @@ uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf if (!is_rx) { if (buflen) { // Copy data from user buffer/fifo to hw buffer - uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + uint8_t *hw_buf = ep->dpram_buf + buf_id * 64; if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL); @@ -257,7 +257,8 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * } // sync endpoint buffer and return transferred bytes -uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, bool is_rx) { +static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, + bool is_rx) { uint32_t buf_ctrl = *buf_ctrl_reg; if (buf_id) { buf_ctrl = buf_ctrl >> 16; @@ -274,7 +275,7 @@ uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_ // we have received AFTER we have copied it to the user buffer at the appropriate offset assert(buf_ctrl & USB_BUF_CTRL_FULL); - uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + uint8_t *hw_buf = ep->dpram_buf + buf_id * 64; if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL); @@ -286,7 +287,7 @@ uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_ ep->xferred_len += xferred_bytes; // Short packet - if (xferred_bytes < ep->wMaxPacketSize) { + if (xferred_bytes < ep->max_packet_size) { // Reduce total length as this is last packet ep->remaining_len = 0; } @@ -312,7 +313,7 @@ static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep, io_rw_32 *ep_reg, // sync buffer 1 if double buffered if (ep_reg != NULL && (*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) { - if (buf0_bytes == ep->wMaxPacketSize) { + if (buf0_bytes == ep->max_packet_size) { // sync buffer 1 if not short packet hwbuf_sync(ep, buf_reg, 1, is_rx); } else { diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index ac1d8610a..cbb2290c1 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -72,8 +72,8 @@ typedef struct hw_endpoint { uint8_t pending; // Transfer scheduled but not active #endif - uint16_t wMaxPacketSize; // max packet size also indicates configured - uint8_t *hw_data_buf; // Buffer pointer in usb dpram + uint16_t max_packet_size; // max packet size also indicates configured + uint8_t *dpram_buf; // Buffer pointer in usb dpram // transfer info union { @@ -111,8 +111,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); -void hw_endpoint_reset_transfer(struct hw_endpoint *ep); void hw_endpoint_start_next_buffer(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); +void hw_endpoint_reset_transfer(struct hw_endpoint *ep); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { // todo add critsec as necessary to prevent issues between worker and IRQ... @@ -123,9 +123,6 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct //--------------------------------------------------------------------+ // Hardware Buffer //--------------------------------------------------------------------+ -uint32_t hwbuf_prepare(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx); -uint16_t hwbuf_sync(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, bool is_rx); - void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { -- cgit v1.3.1 From edcc95de2e0605603d2400239587ea078631076f Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 16 Jan 2026 15:03:41 +0700 Subject: rename --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 6377a5d75..8c2e93805 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -92,22 +92,6 @@ static hcd_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { // static hcd_endpoint_t* epdt_find_interrupt(uint8_t ) -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_host(hw_endpoint_t *ep) { - if (tu_edpt_number(ep->ep_addr) == 0) { - return &usbh_dpram->epx_ctrl; - } - // return &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl; - return NULL; -} - -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_host(hw_endpoint_t *ep) { - if (tu_edpt_number(ep->ep_addr) == 0) { - return &usbh_dpram->epx_buf_ctrl; - } - // return &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl; - return NULL; -} - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -- cgit v1.3.1 From 0dd509ad3639a49f7f6a885da02670c09c05a058 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 16 Jan 2026 16:10:00 +0700 Subject: revert back to shared hw_endpoint_t --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 96 ++++++++++++++-------------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 18 +++--- 2 files changed, 56 insertions(+), 58 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 8c2e93805..0a8dbe11a 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -48,8 +48,8 @@ //--------------------------------------------------------------------+ // Host mode uses one shared endpoint register for non-interrupt endpoint -static hcd_endpoint_t ep_pool[USB_MAX_ENDPOINTS]; -static hcd_endpoint_t *epx = &ep_pool[0]; // current active endpoint +static hw_endpoint_t ep_pool[USB_MAX_ENDPOINTS]; +static hw_endpoint_t *epx = &ep_pool[0]; // current active endpoint // Flags we set by default in sie_ctrl (we add other bits on top) enum { @@ -68,21 +68,21 @@ enum { // //--------------------------------------------------------------------+ -static hcd_endpoint_t *edpt_alloc(void) { +static hw_endpoint_t *edpt_alloc(void) { for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hcd_endpoint_t *ep = &ep_pool[i]; - if (ep->hwep.max_packet_size == 0) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->max_packet_size == 0) { return ep; } } return NULL; } -static hcd_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { +static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { for (uint32_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hcd_endpoint_t *ep = &ep_pool[i]; - if ((ep->dev_addr == daddr) && (ep->hwep.max_packet_size > 0) && - (ep->hwep.ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->hwep.ep_addr) == 0))) { + hw_endpoint_t *ep = &ep_pool[i]; + if ((ep->dev_addr == daddr) && (ep->max_packet_size > 0) && + (ep->ep_addr == ep_addr || (tu_edpt_number(ep_addr) == 0 && tu_edpt_number(ep->ep_addr) == 0))) { return ep; } } @@ -90,7 +90,7 @@ static hcd_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { return NULL; } -// static hcd_endpoint_t* epdt_find_interrupt(uint8_t ) +// static hw_endpoint_t* epdt_find_interrupt(uint8_t ) //--------------------------------------------------------------------+ // @@ -106,17 +106,17 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } -static void __tusb_irq_path_func(hw_xfer_complete)(hcd_endpoint_t *ep, xfer_result_t xfer_result) { +static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; - uint8_t ep_addr = ep->hwep.ep_addr; - uint xferred_len = ep->hwep.xferred_len; - hw_endpoint_reset_transfer(&ep->hwep); + uint8_t ep_addr = ep->ep_addr; + uint xferred_len = ep->xferred_len; + hw_endpoint_reset_transfer(ep); hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); } -static void __tusb_irq_path_func(handle_hwbuf_status_bit)(hcd_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { - const bool done = hw_endpoint_xfer_continue(&ep->hwep, ep_reg, buf_reg); +static void __tusb_irq_path_func(handle_hwbuf_status_bit)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { + const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg); if (done) { hw_xfer_complete(ep, XFER_RESULT_SUCCESS); } @@ -151,7 +151,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { usb_hw_clear->buf_status = bit; for (uint8_t e = 0; e < USB_MAX_ENDPOINTS; e++) { - hcd_endpoint_t *ep = &ep_pool[e]; + hw_endpoint_t *ep = &ep_pool[e]; if (ep->interrupt_num == i) { io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; @@ -168,7 +168,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } } -// +// All non-interrupt endpoints use shared EPX. // static void edpt_scheduler(void) { // } @@ -208,7 +208,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // only handle setup packet if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { - epx->hwep.xferred_len = 8; + epx->xferred_len = 8; hw_xfer_complete(epx, XFER_RESULT_SUCCESS); } else { // Don't care. Will handle this in buff status @@ -231,21 +231,21 @@ void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { hcd_rp2040_irq(); } -static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { +static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { const uint8_t ep_addr = ep_desc->bEndpointAddress; const uint16_t wMaxPacketSize = tu_edpt_packet_size(ep_desc); const uint8_t transfer_type = ep_desc->bmAttributes.xfer; // const uint8_t bmInterval = ep_desc->bInterval; - ep->hwep.max_packet_size = wMaxPacketSize; - ep->hwep.ep_addr = ep_addr; + ep->max_packet_size = wMaxPacketSize; + ep->ep_addr = ep_addr; ep->dev_addr = dev_addr; ep->transfer_type = transfer_type; ep->need_pre = need_pre(dev_addr); - ep->hwep.next_pid = 0u; + ep->next_pid = 0u; if (transfer_type != TUSB_XFER_INTERRUPT) { - ep->hwep.dpram_buf = usbh_dpram->epx_data; + ep->dpram_buf = usbh_dpram->epx_data; } else { // from 15 interrupt endpoints pool uint8_t int_idx; @@ -260,9 +260,9 @@ static void hw_endpoint_init(hcd_endpoint_t *ep, uint8_t dev_addr, const tusb_de //------------- dpram buf -------------// // 15x64 last bytes of DPRAM for interrupt endpoint buffers - ep->hwep.dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); + ep->dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hwep.dpram_buf) | + (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf) | (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; @@ -359,16 +359,16 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)dev_addr; // reset epx if it is currently active with unplugged device - if (epx->hwep.max_packet_size > 0 && epx->dev_addr == dev_addr) { - // if (epx->hwep.active) { + if (epx->max_packet_size > 0 && epx->dev_addr == dev_addr) { + // if (epx->active) { // // need to abort transfer // } - epx->hwep.max_packet_size = 0; + epx->max_packet_size = 0; } for (size_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hcd_endpoint_t *ep = &ep_pool[i]; - if (ep->dev_addr == dev_addr && ep->hwep.max_packet_size > 0) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->dev_addr == dev_addr && ep->max_packet_size > 0) { if (ep->interrupt_num) { // disable interrupt endpoint usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; @@ -378,7 +378,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl = 0; } - ep->hwep.max_packet_size = 0; // mark as unused + ep->max_packet_size = 0; // mark as unused } } } @@ -405,7 +405,7 @@ void hcd_int_disable(uint8_t rhport) { bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { (void)rhport; pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - hcd_endpoint_t *ep = edpt_alloc(); + hw_endpoint_t *ep = edpt_alloc(); TU_ASSERT(ep); hw_endpoint_init(ep, dev_addr, ep_desc); @@ -431,26 +431,26 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { } // xfer using epx -static void edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { +static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { if (ep->transfer_type == TUSB_XFER_INTERRUPT) { // For interrupt endpoint control and buffer is already configured // Note: Interrupt is single buffered only io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; - hw_endpoint_xfer_start(&ep->hwep, ep_reg, buf_reg, buffer, ff, total_len); + hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); } else { - const uint8_t ep_num = tu_edpt_number(ep->hwep.ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->hwep.ep_addr); + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); // ep control - const uint32_t dpram_offset = hw_data_offset(ep->hwep.dpram_buf); + const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; usbh_dpram->epx_ctrl = ep_ctrl; io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - hw_endpoint_xfer_start(&ep->hwep, ep_reg, buf_reg, buffer, ff, total_len); + hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); // addr control usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); @@ -467,13 +467,13 @@ static void edpt_xfer(hcd_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16 bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void)rhport; - hcd_endpoint_t *ep = edpt_find(dev_addr, ep_addr); + hw_endpoint_t *ep = edpt_find(dev_addr, ep_addr); TU_ASSERT(ep); // Control endpoint can change direction 0x00 <-> 0x80 - if (ep_addr != ep->hwep.ep_addr) { - ep->hwep.ep_addr = ep_addr; - ep->hwep.next_pid = 1; // data and status stage start with DATA1 + if (ep_addr != ep->ep_addr) { + ep->ep_addr = ep_addr; + ep->next_pid = 1; // data and status stage start with DATA1 } edpt_xfer(ep, buffer, NULL, buflen); @@ -497,13 +497,13 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet usbh_dpram->setup_packet[i] = setup_packet[i]; } - hcd_endpoint_t *ep = edpt_find(dev_addr, 0x00); + hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); - ep->hwep.ep_addr = 0; // setup is OUT - ep->hwep.remaining_len = 8; - ep->hwep.xferred_len = 0; - ep->hwep.active = true; + ep->ep_addr = 0; // setup is OUT + ep->remaining_len = 8; + ep->xferred_len = 0; + ep->active = true; epx = ep; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index cbb2290c1..46bd727c8 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -65,11 +65,18 @@ typedef struct hw_endpoint { uint8_t ep_addr; uint8_t next_pid; bool active; // transferring data + uint8_t pending; // Transfer scheduled but not active bool is_xfer_fifo; // transfer using fifo #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX bool e15_bulk_in; // Errata15 device bulk in - uint8_t pending; // Transfer scheduled but not active +#endif + +#if CFG_TUH_ENABLED + uint8_t dev_addr; + uint8_t interrupt_num; // 1-15 for interrupt endpoints + uint8_t transfer_type; + bool need_pre; // need preamble for low speed device behind full speed hub #endif uint16_t max_packet_size; // max packet size also indicates configured @@ -85,15 +92,6 @@ typedef struct hw_endpoint { } hw_endpoint_t; -// Host controller endpoint -typedef struct { - hw_endpoint_t hwep; - uint8_t dev_addr; - uint8_t interrupt_num; // 1-15 for interrupt endpoints - uint8_t transfer_type; - bool need_pre; // need preamble for low speed device behind full speed hub -} hcd_endpoint_t; - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX extern volatile uint32_t e15_last_sof; #endif -- cgit v1.3.1 From 9b17d55c91744ace59ad85dbb7b4ae8c6aedfb2c Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 12 Feb 2026 20:28:40 +0700 Subject: rp2 disable hwfifo --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 2 ++ src/portable/raspberrypi/rp2040/rp2040_usb.c | 18 +++++++++++++++--- src/tusb_option.h | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 240e6c727..ca0e24e96 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -505,6 +505,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to return true; } +#if CFG_TUD_EDPT_DEDICATED_HWFIFO bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) { (void)rhport; (void)is_isr; @@ -512,6 +513,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t hw_endpoint_xfer_start(ep, NULL, ff, total_bytes); return true; } +#endif void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 3b65f57a4..ca83acc6b 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -53,6 +53,7 @@ static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) { } } +#if CFG_TUD_EDPT_DEDICATED_HWFIFO void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) { (void)access_mode; unaligned_memcpy((uint8_t *)(uintptr_t)hwfifo, src, len); @@ -62,6 +63,7 @@ void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, co (void)access_mode; unaligned_memcpy(dest, (const uint8_t *)(uintptr_t)hwfifo, len); } +#endif void rp2usb_init(void) { // Reset usb controller @@ -138,10 +140,13 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, if (buflen) { // Copy data from user buffer/fifo to hw buffer uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL); - } else { + } else + #endif + { unaligned_memcpy(hw_buf, ep->user_buf, buflen); ep->user_buf += buflen; } @@ -227,6 +232,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) } void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { + (void) ff; hw_endpoint_lock_update(ep, 1); if (ep->active) { @@ -240,10 +246,13 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, tu_fifo_t * ep->xferred_len = 0; ep->active = true; +#if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ff != NULL) { ep->user_fifo = ff; ep->is_xfer_fifo = true; - } else { + } else +#endif + { ep->user_buf = buffer; ep->is_xfer_fifo = false; } @@ -284,10 +293,13 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(hw_endpoint_t *ep, io_rw_32 assert(buf_ctrl & USB_BUF_CTRL_FULL); uint8_t *hw_buf = ep->hw_data_buf + buf_id * 64; + #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL); - } else { + } else + #endif + { unaligned_memcpy(ep->user_buf, hw_buf, xferred_bytes); ep->user_buf += xferred_bytes; } diff --git a/src/tusb_option.h b/src/tusb_option.h index 9eb8ee533..74a556605 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -381,7 +381,7 @@ #endif #if (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB - #define CFG_TUD_EDPT_DEDICATED_HWFIFO 1 + #define CFG_TUD_EDPT_DEDICATED_HWFIFO 0 #define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE 1 #define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 1 #define CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE -- cgit v1.3.1 From 0c6fa9bd7f4d2619a538fcfd866ef6150fa2adeb Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 Mar 2026 22:59:22 +0700 Subject: Add EPX transfer scheduling when hcd_edpt_xfer() is called while epx is busy --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 105 +++++++++++++++++++++++++-- src/portable/raspberrypi/rp2040/rp2040_usb.c | 12 ++- test/hil/hil_test.py | 11 ++- 3 files changed, 116 insertions(+), 12 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 0a8dbe11a..b5c4422e2 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -106,6 +106,9 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } +// forward declaration +static void __tusb_irq_path_func(edpt_schedule_next)(void); + static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; @@ -113,6 +116,11 @@ static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_resul uint xferred_len = ep->xferred_len; hw_endpoint_reset_transfer(ep); hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); + + // Schedule next pending EPX transfer (only for non-interrupt endpoints) + if (ep == epx) { + edpt_schedule_next(); + } } static void __tusb_irq_path_func(handle_hwbuf_status_bit)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { @@ -169,8 +177,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } // All non-interrupt endpoints use shared EPX. -// static void edpt_scheduler(void) { -// } +// Forward declared above hw_xfer_complete, defined after edpt_xfer below. static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { const uint32_t status = usb_hw->ints; @@ -206,7 +213,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { if (status & USB_INTS_TRANS_COMPLETE_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; - // only handle setup packet + // only handle a setup packet if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { epx->xferred_len = 8; hw_xfer_complete(epx, XFER_RESULT_SUCCESS); @@ -215,6 +222,30 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } } + // if (status & USB_INTS_EP_STALL_NAK_BITS) { + // const uint32_t ep_stall_nak = usb_hw->ep_status_stall_nak; + // usb_hw->ep_status_stall_nak = ep_stall_nak; // clear by writing back (WC) + // + // // Preempt non-control EPX bulk transfer when a different ep is pending + // if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { + // for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + // hw_endpoint_t *ep = &ep_pool[i]; + // if (ep->pending && ep != epx) { + // // Stop current transaction + // usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; + // + // // Mark current EPX as pending to resume later + // epx->pending = 1; + // epx->active = false; + // + // // Start the next pending transfer + // edpt_schedule_next(); + // break; + // } + // } + // } + // } + if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; } @@ -250,7 +281,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_des // from 15 interrupt endpoints pool uint8_t int_idx; for (int_idx = 0; int_idx < USB_HOST_INTERRUPT_ENDPOINTS; int_idx++) { - if (!tu_bit_test(usb_hw_set->int_ep_ctrl, 1 + int_idx)) { + if (!tu_bit_test(usb_hw->int_ep_ctrl, 1 + int_idx)) { ep->interrupt_num = int_idx + 1; break; } @@ -313,6 +344,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { USB_INTE_HOST_RESUME_BITS | USB_INTE_STALL_BITS | USB_INTE_TRANS_COMPLETE_BITS | + // USB_INTE_EP_STALL_NAK_BITS | USB_INTE_ERROR_RX_TIMEOUT_BITS | USB_INTE_ERROR_DATA_SEQ_BITS ; @@ -369,6 +401,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { for (size_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hw_endpoint_t *ep = &ep_pool[i]; if (ep->dev_addr == dev_addr && ep->max_packet_size > 0) { + ep->pending = 0; // clear any pending transfer + if (ep->interrupt_num) { // disable interrupt endpoint usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; @@ -442,10 +476,18 @@ static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_ const uint8_t ep_num = tu_edpt_number(ep->ep_addr); const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); + // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. + // The buffer selector toggles even in single-buffered mode, so the previous transfer's status + // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. +#if defined(PICO_RP2040) && PICO_RP2040 == 1 + usbh_dpram->epx_buf_ctrl = 0; +#endif + // ep control const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset /*| + (1u << 16)*/; // INTERRUPT_ON_NAK usbh_dpram->epx_ctrl = ep_ctrl; io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; @@ -464,6 +506,40 @@ static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_ } } +// Schedule next pending EPX transfer from ISR context +static void __tusb_irq_path_func(edpt_schedule_next)(void) { + // EPX may already be active if the completion callback started a new transfer + if (epx->active) return; + + for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->pending == 0) continue; + + if (ep->pending == 2) { + // Pending setup: DPRAM already has the setup packet + ep->ep_addr = 0; + ep->remaining_len = 8; + ep->xferred_len = 0; + ep->active = true; + ep->pending = 0; + + epx = ep; + usb_hw->dev_addr_ctrl = ep->dev_addr; + + const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | + (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); + } else { + // Pending data transfer: preserve partial progress from preemption + uint16_t prev_xferred = ep->xferred_len; + ep->pending = 0; + edpt_xfer(ep, ep->user_buf, NULL, ep->remaining_len); + epx->xferred_len += prev_xferred; // restore partial progress + } + return; // start only one transfer + } +} + bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void)rhport; @@ -476,6 +552,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b ep->next_pid = 1; // data and status stage start with DATA1 } + // If EPX is busy with another transfer, mark as pending + if (ep->transfer_type != TUSB_XFER_INTERRUPT && epx->active) { + ep->user_buf = buffer; + ep->remaining_len = buflen; + ep->pending = 1; + return true; + } + edpt_xfer(ep, buffer, NULL, buflen); return true; @@ -492,7 +576,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { (void)rhport; - // Copy data into setup packet buffer + // Copy data into setup packet buffer (usbh only schedules one setup at a time) for (uint8_t i = 0; i < 8; i++) { usbh_dpram->setup_packet[i] = setup_packet[i]; } @@ -500,7 +584,14 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); - ep->ep_addr = 0; // setup is OUT + ep->ep_addr = 0; // setup is OUT + + // If EPX is busy, mark as pending setup (DPRAM already has the packet) + if (epx->active) { + ep->pending = 2; + return true; + } + ep->remaining_len = 8; ep->xferred_len = 0; ep->active = true; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index f2d4800e1..0f1075eda 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -110,7 +110,17 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an value |= or_mask; if (or_mask & USB_BUF_CTRL_AVAIL) { if (buf_ctrl & USB_BUF_CTRL_AVAIL) { - panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_ctrl_reg); + if (is_host) { +#if defined(PICO_RP2040) && PICO_RP2040 == 1 + // RP2040-E4: host buffer selector toggles in single-buffered mode, causing status + // to be written to BUF1 half and leaving stale AVAILABLE in BUF0 half. Clear it. + *buf_ctrl_reg = 0; +#else + panic("buf_ctrl @ 0x%lX already available (host)", (uintptr_t)buf_ctrl_reg); +#endif + } else { + panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_ctrl_reg); + } } *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL; diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 41e9fad88..b3458e59d 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -475,7 +475,7 @@ def test_host_cdc_msc_hid(board): cdc_devs = [d for d in dev_attached if d.get('is_cdc')] msc_devs = [d for d in dev_attached if d.get('is_msc')] if not cdc_devs and not msc_devs: - return + return 'skipped' port = get_serial_dev(flasher["uid"], None, None, 0) ser = open_serial_dev(port) @@ -568,7 +568,7 @@ def test_host_msc_file_explorer(board): flasher = board['flasher'] msc_devs = [d for d in board['tests'].get('dev_attached', []) if d.get('is_msc')] if not msc_devs: - return + return 'skipped' port = get_serial_dev(flasher["uid"], None, None, 0) ser = open_serial_dev(port) @@ -1113,8 +1113,11 @@ def test_example(board, f1, example): ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name) if ret.returncode == 0: try: - globals()[f'test_{example.replace("/", "_")}'](board) - print(' OK', end='') + tret = globals()[f'test_{example.replace("/", "_")}'](board) + if tret == 'skipped': + print(f' {STATUS_SKIPPED}', end='') + else: + print(' OK', end='') break except Exception as e: if i == max_rety - 1: -- cgit v1.3.1 From f7d1c10b73e2f549d9727b4a8a11c1ca58906881 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 20 Mar 2026 00:50:55 +0700 Subject: Add support for EPX preemption on RP2350 during NAK conditions --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 112 +++++++++++++++++++++------ 1 file changed, 88 insertions(+), 24 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index b5c4422e2..f3153d0e9 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -108,6 +108,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { // forward declaration static void __tusb_irq_path_func(edpt_schedule_next)(void); +TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value); +static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack @@ -222,29 +224,79 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } } - // if (status & USB_INTS_EP_STALL_NAK_BITS) { - // const uint32_t ep_stall_nak = usb_hw->ep_status_stall_nak; - // usb_hw->ep_status_stall_nak = ep_stall_nak; // clear by writing back (WC) - // - // // Preempt non-control EPX bulk transfer when a different ep is pending - // if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { - // for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - // hw_endpoint_t *ep = &ep_pool[i]; - // if (ep->pending && ep != epx) { - // // Stop current transaction - // usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; - // - // // Mark current EPX as pending to resume later - // epx->pending = 1; - // epx->active = false; - // - // // Start the next pending transfer - // edpt_schedule_next(); - // break; - // } - // } - // } - // } +#if defined(PICO_RP2350) && PICO_RP2350 == 1 + if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { + // RP2350: EPX transfer stopped due to NAK from device. + // Clear EPX_STOPPED_ON_NAK status (WC) + usb_hw->nak_poll |= USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; + + bool preempted = false; + + // Only preempt non-control endpoints + if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { + // Find the next pending transfer (different from the current epx) + for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->pending && ep != epx) { + // NAK means no data transferred. Restore remaining_len from buffer control + // so edpt_schedule_next can properly resume this transfer later. + const uint16_t buf0_len = usbh_dpram->epx_buf_ctrl & USB_BUF_CTRL_LEN_MASK; + epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); + epx->next_pid ^= 1u; // undo PID toggle from hwbuf_prepare + if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { + epx->user_buf -= buf0_len; // undo buffer advance for OUT + } + + // Mark current EPX as pending to resume later + epx->pending = 1; + epx->active = false; + + // Clear EPX buffer control - AVAILABLE is still set from the NAK'd transfer + usbh_dpram->epx_buf_ctrl = 0; + + // Start the found pending transfer directly + if (ep->pending == 2) { + // Pending setup: DPRAM already has the setup packet + ep->ep_addr = 0; + ep->remaining_len = 8; + ep->xferred_len = 0; + ep->active = true; + ep->pending = 0; + + epx = ep; + usb_hw->dev_addr_ctrl = ep->dev_addr; + + const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS | + (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sc); + } else { + // Pending data transfer: preserve partial progress + uint16_t prev_xferred = ep->xferred_len; + ep->pending = 0; + edpt_xfer(ep, ep->user_buf, NULL, ep->remaining_len); + epx->xferred_len += prev_xferred; + } + + preempted = true; + break; + } + } + } + + if (!preempted && epx->active) { + // No preemption needed: disable stop-on-NAK and restart the transaction. + // Buffer control still has AVAILABLE set, just re-trigger START_TRANS. + uint32_t nak_poll = usb_hw->nak_poll; + nak_poll &= ~USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; + usb_hw->nak_poll = nak_poll; + + const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr); + const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); + } + } +#endif if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; @@ -344,10 +396,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { USB_INTE_HOST_RESUME_BITS | USB_INTE_STALL_BITS | USB_INTE_TRANS_COMPLETE_BITS | - // USB_INTE_EP_STALL_NAK_BITS | USB_INTE_ERROR_RX_TIMEOUT_BITS | USB_INTE_ERROR_DATA_SEQ_BITS ; +#if defined(PICO_RP2350) && PICO_RP2350 == 1 + // RP2350: Enable EPX stopped-on-NAK interrupt (feature is enabled dynamically when transfers are pending) + usb_hw_set->inte = USB_INTE_EPX_STOPPED_ON_NAK_BITS; +#endif + return true; } @@ -557,6 +613,10 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b ep->user_buf = buffer; ep->remaining_len = buflen; ep->pending = 1; +#if defined(PICO_RP2350) && PICO_RP2350 == 1 + // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted + usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; +#endif return true; } @@ -589,6 +649,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet // If EPX is busy, mark as pending setup (DPRAM already has the packet) if (epx->active) { ep->pending = 2; +#if defined(PICO_RP2350) && PICO_RP2350 == 1 + // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted + usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; +#endif return true; } -- cgit v1.3.1 From f7cf6bc581c514984d3513bea8538e33e7d726d8 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 20 Mar 2026 19:36:57 +0700 Subject: clean up --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 31 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index f3153d0e9..3c250ae7f 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -30,11 +30,15 @@ #if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB && !CFG_TUH_MAX3421 #include "pico.h" -#include "rp2040_usb.h" + +#if defined(PICO_RP2350) && PICO_RP2350 == 1 +#define HAS_STOP_EPX_ON_NAK +#endif //--------------------------------------------------------------------+ // INCLUDE //--------------------------------------------------------------------+ +#include "rp2040_usb.h" #include "osal/osal.h" #include "host/hcd.h" @@ -224,11 +228,11 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } } -#if defined(PICO_RP2350) && PICO_RP2350 == 1 +#ifdef HAS_STOP_EPX_ON_NAK if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { - // RP2350: EPX transfer stopped due to NAK from device. + // EPX transfer stopped due to NAK from the device. // Clear EPX_STOPPED_ON_NAK status (WC) - usb_hw->nak_poll |= USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; + usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; bool preempted = false; @@ -286,9 +290,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { if (!preempted && epx->active) { // No preemption needed: disable stop-on-NAK and restart the transaction. // Buffer control still has AVAILABLE set, just re-trigger START_TRANS. - uint32_t nak_poll = usb_hw->nak_poll; - nak_poll &= ~USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; - usb_hw->nak_poll = nak_poll; + usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr); const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | @@ -399,8 +401,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { USB_INTE_ERROR_RX_TIMEOUT_BITS | USB_INTE_ERROR_DATA_SEQ_BITS ; -#if defined(PICO_RP2350) && PICO_RP2350 == 1 - // RP2350: Enable EPX stopped-on-NAK interrupt (feature is enabled dynamically when transfers are pending) +#ifdef HAS_STOP_EPX_ON_NAK usb_hw_set->inte = USB_INTE_EPX_STOPPED_ON_NAK_BITS; #endif @@ -613,8 +614,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b ep->user_buf = buffer; ep->remaining_len = buflen; ep->pending = 1; -#if defined(PICO_RP2350) && PICO_RP2350 == 1 - // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted +#ifdef HAS_STOP_EPX_ON_NAK + // Enable stop-on-NAK to round-robin when NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; #endif return true; @@ -649,8 +650,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet // If EPX is busy, mark as pending setup (DPRAM already has the packet) if (epx->active) { ep->pending = 2; -#if defined(PICO_RP2350) && PICO_RP2350 == 1 - // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted +#ifdef HAS_STOP_EPX_ON_NAK + // Enable stop-on-NAK to round-robin when NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; #endif return true; @@ -661,9 +662,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet ep->active = true; epx = ep; - - // Set device address - usb_hw->dev_addr_ctrl = dev_addr; + usb_hw->dev_addr_ctrl = dev_addr; // Set device address // Set pre if we are a low speed device on full speed hub const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); -- cgit v1.3.1 From 15eef94df0e759adf14697197a89dddeb8fc3aeb Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 23 Mar 2026 15:49:58 +0700 Subject: add rp2040 sof + stop_trans on nak. increase nak_poll fs/ls delay to 300 us to prevent xfer is ack while stopping. --- examples/host/cdc_msc_hid/src/msc_app.c | 2 +- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 163 ++++++++++++++++----------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 10 +- 3 files changed, 105 insertions(+), 70 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c index 4a85e46d7..8181bdcb9 100644 --- a/examples/host/cdc_msc_hid/src/msc_app.c +++ b/examples/host/cdc_msc_hid/src/msc_app.c @@ -41,7 +41,7 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const } // Print out Vendor ID, Product ID and Rev - printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev); + printf("%.8s %.16s %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev); // Get capacity of device uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun); diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 3c250ae7f..9ee15d343 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -185,6 +185,54 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { // All non-interrupt endpoints use shared EPX. // Forward declared above hw_xfer_complete, defined after edpt_xfer below. +// Save current EPX context, mark pending, switch to next_ep +static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { + const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; + const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; + epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); + epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0; + if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { + epx->user_buf -= buf0_len; + } + epx->pending = 1; + epx->active = false; + usbh_dpram->epx_buf_ctrl = 0; + + if (next_ep->pending == 2) { + next_ep->ep_addr = 0; + next_ep->remaining_len = 8; + next_ep->xferred_len = 0; + next_ep->active = true; + next_ep->pending = 0; + epx = next_ep; + usb_hw->dev_addr_ctrl = next_ep->dev_addr; + const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS | + (next_ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sc); + } else { + uint16_t prev_xferred = next_ep->xferred_len; + next_ep->pending = 0; + edpt_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); + epx->xferred_len += prev_xferred; + } +} + +// Round-robin find next pending ep after current epx +static hw_endpoint_t *__tusb_irq_path_func(epx_find_pending)(void) { + const uint start = (uint)(epx - &ep_pool[0]) + 1; + for (uint i = start; i < TU_ARRAY_SIZE(ep_pool); i++) { + if (ep_pool[i].pending) { + return &ep_pool[i]; + } + } + for (uint i = 0; i < start - 1; i++) { + if (ep_pool[i].pending) { + return &ep_pool[i]; + } + } + return NULL; +} + static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { const uint32_t status = usb_hw->ints; @@ -229,73 +277,51 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } #ifdef HAS_STOP_EPX_ON_NAK + // RP2350: hardware stops EPX on NAK automatically if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { - // EPX transfer stopped due to NAK from the device. - // Clear EPX_STOPPED_ON_NAK status (WC) usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; - bool preempted = false; - - // Only preempt non-control endpoints + hw_endpoint_t *next_ep = NULL; if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { - // Find the next pending transfer (different from the current epx) - for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hw_endpoint_t *ep = &ep_pool[i]; - if (ep->pending && ep != epx) { - // NAK means no data transferred. Restore remaining_len from buffer control - // so edpt_schedule_next can properly resume this transfer later. - const uint16_t buf0_len = usbh_dpram->epx_buf_ctrl & USB_BUF_CTRL_LEN_MASK; - epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); - epx->next_pid ^= 1u; // undo PID toggle from hwbuf_prepare - if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { - epx->user_buf -= buf0_len; // undo buffer advance for OUT - } - - // Mark current EPX as pending to resume later - epx->pending = 1; - epx->active = false; - - // Clear EPX buffer control - AVAILABLE is still set from the NAK'd transfer - usbh_dpram->epx_buf_ctrl = 0; - - // Start the found pending transfer directly - if (ep->pending == 2) { - // Pending setup: DPRAM already has the setup packet - ep->ep_addr = 0; - ep->remaining_len = 8; - ep->xferred_len = 0; - ep->active = true; - ep->pending = 0; - - epx = ep; - usb_hw->dev_addr_ctrl = ep->dev_addr; - - const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS | - (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sc); - } else { - // Pending data transfer: preserve partial progress - uint16_t prev_xferred = ep->xferred_len; - ep->pending = 0; - edpt_xfer(ep, ep->user_buf, NULL, ep->remaining_len); - epx->xferred_len += prev_xferred; - } - - preempted = true; - break; - } - } + next_ep = epx_find_pending(); } - - if (!preempted && epx->active) { - // No preemption needed: disable stop-on-NAK and restart the transaction. - // Buffer control still has AVAILABLE set, just re-trigger START_TRANS. + if (next_ep) { + epx_switch_ep(next_ep); + } else { + // No preemption: disable stop-on-NAK, restart current transfer usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; - - const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr); - const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); + if (epx->active) { + const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr); + const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); + } + } + } +#else + // RP2040: on SOF, stop and switch if there's a pending ep + if (status & USB_INTS_HOST_SOF_BITS) { + (void) usb_hw->sof_rd; // clear SOF by reading SOF_RD + if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { + hw_endpoint_t *next_ep = epx_find_pending(); + if (next_ep) { + usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; + while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} + busy_wait_at_least_cycles(12); + if (usb_hw->buf_status & 1u) { + usb_hw->nak_poll = USB_NAK_POLL_RESET; + handle_hwbuf_status(); + } else { + epx_switch_ep(next_ep); + } + } else { + usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; + usb_hw->nak_poll = USB_NAK_POLL_RESET; + } + } else if (!epx_find_pending()) { + // EPX is on control endpoint or inactive — disable SOF if nothing pending + usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; + usb_hw->nak_poll = USB_NAK_POLL_RESET; } } #endif @@ -615,8 +641,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b ep->remaining_len = buflen; ep->pending = 1; #ifdef HAS_STOP_EPX_ON_NAK - // Enable stop-on-NAK to round-robin when NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; +#else + // Only enable SOF preemption for non-control endpoints + if (tu_edpt_number(epx->ep_addr) != 0) { + usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | + (300 << USB_NAK_POLL_DELAY_LS_LSB); + usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; + } #endif return true; } @@ -651,8 +683,13 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet if (epx->active) { ep->pending = 2; #ifdef HAS_STOP_EPX_ON_NAK - // Enable stop-on-NAK to round-robin when NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; +#else + if (tu_edpt_number(epx->ep_addr) != 0) { + usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | + (300 << USB_NAK_POLL_DELAY_LS_LSB); + usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; + } #endif return true; } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 0f1075eda..66e579c39 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -124,12 +124,10 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an } *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL; - // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, we need to - // wait at least 1/48 mhz (usb clock), 12 cycles should be good for 48*12Mhz = 576Mhz. - // Don't need delay in host mode as host is in charge - if (!is_host) { - busy_wait_at_least_cycles(12); - } + // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, + // wait for USB controller to see the update before setting AVAILABLE. + // Host also needs this for continuation buffers in multi-packet transfers. + busy_wait_at_least_cycles(12); } } -- cgit v1.3.1 From 09197ed27929d03a7f16e3d67dc76fa312948316 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 24 Mar 2026 16:05:31 +0700 Subject: handle buf_status in per buffer basic (INTERRUPT_PER_BUFFER), this allows us to sync/move half data payload instead of waiting for pair complete. Refactor endpoint control and buffer handling functions for clarity and efficiency. --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 130 +++++++++++----------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 154 +++++++++------------------ src/portable/raspberrypi/rp2040/rp2040_usb.h | 10 +- 3 files changed, 128 insertions(+), 166 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index e4d56f69a..ec73cafc1 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -69,20 +69,18 @@ TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8 return hw_endpoint_get(num, dir); } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) { - const uint8_t epnum = tu_edpt_number(ep->ep_addr); - const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_ep_ctrl(const uint8_t epnum, tusb_dir_t dir) { if (epnum == 0) { // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled return NULL; } - return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out; + struct usb_device_dpram_ep_ctrl *ep_ctrl = &usb_dpram->ep_ctrl[epnum - 1]; + return (dir == TUSB_DIR_IN) ? &ep_ctrl->in : &ep_ctrl->out; } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) { - const uint8_t epnum = tu_edpt_number(ep->ep_addr); - const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); - return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out; +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_buf_ctrl(const uint8_t epnum, tusb_dir_t dir) { + struct usb_device_dpram_ep_buf_ctrl *buf_ctrl = &usb_dpram->ep_buf_ctrl[epnum]; + return (dir == TUSB_DIR_IN) ? &buf_ctrl->in : &buf_ctrl->out; } // main processing for dcd_edpt_iso_activate @@ -92,11 +90,12 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa ep->max_packet_size = wMaxPacketSize; // Clear existing buffer control state - io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); *buf_ctrl_reg = 0; // allocated hw buffer - const uint8_t epnum = tu_edpt_number(ep_addr); if (epnum == 0) { // Buffer offset is fixed (also double buffered) ep->dpram_buf = (uint8_t *)&usb_dpram->ep0_buf_a[0]; @@ -109,7 +108,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa size *= 2u; #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + if (dir == TUSB_DIR_IN) { ep->e15_bulk_in = true; } #endif @@ -124,13 +123,13 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa } } -static void hw_endpoint_enable(hw_endpoint_t *ep, uint8_t transfer_type) { - io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep); +static void hw_endpoint_enable(uint8_t epnum, tusb_dir_t dir, uint8_t transfer_type, uint8_t *dpram_buf) { + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); // Set endpoint control register to enable (EP0 has no endpoint control register) - if (ctrl_reg != NULL) { + if (ep_reg != NULL) { const uint32_t ctrl_value = - EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf); - *ctrl_reg = ctrl_value; + EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(dpram_buf); + *ep_reg = ctrl_value; } } @@ -141,7 +140,7 @@ static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type); - hw_endpoint_enable(ep, transfer_type); + hw_endpoint_enable(epnum, dir, transfer_type, ep->dpram_buf); } static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { @@ -162,7 +161,7 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { buf_ctrl |= USB_BUF_CTRL_DATA1_PID; } - io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); hw_endpoint_reset_transfer(ep); @@ -173,32 +172,41 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { } static void __tusb_irq_path_func(handle_hw_buff_status)(void) { - uint32_t remaining_buffers = usb_hw->buf_status; - pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); - uint bit = 1u; - for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) { - if (remaining_buffers & bit) { - // clear this in advance + uint32_t buf_status = usb_hw->buf_status; + pico_trace("buf_status = 0x%08lx\r\n", buf_status); + while (buf_status) { + // ctz/clz is faster than loop which has only a few bit set in general + const uint8_t i = (uint8_t) __builtin_ctz(buf_status); + const uint bit = TU_BIT(i); + + // Read which buffer to handle BEFORE clearing buf_status + uint8_t buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; + usb_hw_clear->buf_status = bit; + + // IN transfer for even i, OUT transfer for odd i + const uint8_t epnum = i >> 1u; + const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id); + + // Double-buffered: if both buffers completed at once, buf_status re-sets + // immediately after clearing (datasheet Table 406). Process the second buffer too. + if (!done && (usb_hw->buf_status & bit)) { + buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; usb_hw_clear->buf_status = bit; + done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id); + } - // IN transfer for even i, OUT transfer for odd i - const uint8_t epnum = i >> 1u; - const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; - hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - - io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); - io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); - const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg); - - if (done) { - // Notify usbd - const uint16_t xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); - dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); - } - remaining_buffers &= ~bit; + if (done) { + const uint16_t xferred_len = ep->xferred_len; + hw_endpoint_reset_transfer(ep); + dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); } - bit <<= 1u; + + buf_status &= ~bit; } } @@ -251,9 +259,9 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { hw_endpoint_lock_update(ep, 1); if (ep->pending) { ep->pending = 0; - io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); - io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); - hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); + io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); + io_rw_32 *buf_reg = get_buf_ctrl(i, TUSB_DIR_IN); + hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); } hw_endpoint_lock_update(ep, -1); } @@ -361,7 +369,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { (void) rh_init; assert(rhport == 0); - TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version()); + TU_LOG(1, "Chip Version B%u\r\n", rp2040_chip_version()); // Reset hardware to default state rp2usb_init(); @@ -508,7 +516,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) } ep->max_packet_size = ep_desc->wMaxPacketSize; - hw_endpoint_enable(ep, TUSB_XFER_ISOCHRONOUS); + hw_endpoint_enable(epnum, dir, TUSB_XFER_ISOCHRONOUS, ep->dpram_buf); return true; } @@ -521,9 +529,12 @@ void dcd_edpt_close_all(uint8_t rhport) { bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { (void)rhport; (void)is_isr; - hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); - io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); - io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, total_bytes); return true; } @@ -532,9 +543,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) { (void)rhport; (void)is_isr; - hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr); - io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep); - io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep); + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); hw_endpoint_xfer_start(ep, ep_reg, buf_reg, NULL, ff, total_bytes); return true; } @@ -544,7 +555,6 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; const uint8_t epnum = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); - hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); if (epnum == 0) { // A stall on EP0 has to be armed so it can be cleared on the next setup packet @@ -552,19 +562,19 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { } // stall and clear current pending buffer, may need to use EP_ABORT - io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); hwbuf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; + const uint8_t epnum = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); - if (tu_edpt_number(ep_addr)) { - struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - - // clear stall also reset toggle to DATA0, ready for next transfer - ep->next_pid = 0; - io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep); + if (epnum != 0) { + struct hw_endpoint* ep = hw_endpoint_get(epnum, dir); + ep->next_pid = 0; // reset data toggle + io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); hwbuf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL); } } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 66e579c39..ac1536f5b 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -35,8 +35,6 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ -static void sync_xfer(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX static bool e15_is_critical_frame_period(struct hw_endpoint *ep); #else @@ -137,18 +135,18 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an // prepare buffer, move data if tx, return buffer control static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); - ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); + ep->remaining_len -= buflen; uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; - - // PID - buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; + if (ep->next_pid) { + buf_ctrl |= USB_BUF_CTRL_DATA1_PID; + } ep->next_pid ^= 1u; if (!is_rx) { if (buflen) { // Copy data from user buffer/fifo to hw buffer - uint8_t *hw_buf = ep->dpram_buf + buf_id * 64; + uint8_t *hw_buf = ep->dpram_buf + (buf_id << 6); #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround @@ -161,7 +159,6 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint } } - // Mark as full buf_ctrl |= USB_BUF_CTRL_FULL; } @@ -172,15 +169,11 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint buf_ctrl |= USB_BUF_CTRL_LAST; } - if (buf_id) { - buf_ctrl = buf_ctrl << 16; - } - return buf_ctrl; } -// Prepare buffer control register value -void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { +// Start transaction on hw buffer +void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); const bool is_host = rp2usb_is_host_mode(); @@ -194,39 +187,34 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep, // always compute and start with buffer 0 uint32_t buf_ctrl = hwbuf_prepare(ep, 0, is_rx) | USB_BUF_CTRL_SEL; - // EP0 has no endpoint control register, also usbd only schedule 1 packet at a time (single buffer) + // Device mode EP0 has no endpoint control register if (ep_reg != NULL) { - uint32_t ep_ctrl = *ep_reg; - - // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause - // short packet on buffer0 - // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single - // buffered, - // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint - const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0); + // Each buffer completion triggers its own IRQ. + // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406). + uint32_t ep_ctrl = *ep_reg | EP_CTRL_INTERRUPT_PER_BUFFER; + + // Since short packet on buf0 in double-buffered RX: buf1 may already contain data from the + // NEXT transfer (host sent it before CPU processed this IRQ). Cannot safely recover. Avoid by not using double + // buffering for rx transfer + bool force_single = is_rx; + #if CFG_TUH_ENABLED + force_single |= (is_host && ep->interrupt_num != 0); // host interrupt is single only + #endif if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) - - buf_ctrl |= hwbuf_prepare(ep, 1, is_rx); - - // Set endpoint control double buffered bit if needed - ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; - ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; + buf_ctrl |= (hwbuf_prepare(ep, 1, is_rx) << 16); + ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS; } else { // Single buffered since 1 is enough - ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); - ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; + ep_ctrl &= ~EP_CTRL_DOUBLE_BUFFERED_BITS; } *ep_reg = ep_ctrl; } - TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); - - // Finally, write to buffer_control which will trigger the transfer - // the next time the controller polls this dpram address + // Finally, write to buffer_control which will trigger the transfer the next time the controller polls this endpoint hwbuf_ctrl_set(buf_reg, buf_ctrl); } @@ -267,7 +255,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * } else #endif { - hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); + hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); } hw_endpoint_lock_update(ep, -1); @@ -315,91 +303,49 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *bu return xferred_bytes; } -// Update hw endpoint struct with info from hardware after a buff status interrupt -static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { - // const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - const bool is_host = rp2usb_is_host_mode(); - bool is_rx; - - if (is_host) { - is_rx = (dir == TUSB_DIR_IN); - } else { - is_rx = (dir == TUSB_DIR_OUT); - } - - TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_reg), tu_u32_high16(*buf_reg)); - uint16_t buf0_bytes = hwbuf_sync(ep, buf_reg, 0, is_rx); // always sync buffer 0 - - // sync buffer 1 if double buffered - if (ep_reg != NULL && (*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) { - if (buf0_bytes == ep->max_packet_size) { - // sync buffer 1 if not short packet - hwbuf_sync(ep, buf_reg, 1, is_rx); - } else { - // short packet on buffer 0 - // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example - // At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from - // the next transfer (not current one). For now we disable double buffered for device OUT - // NOTE this could happen to Host IN -#if 0 - uint8_t const ep_num = tu_edpt_number(ep->ep_addr); - uint8_t const dir = (uint8_t) tu_edpt_dir(ep->ep_addr); - uint8_t const ep_id = 2*ep_num + (dir ? 0 : 1); - - // abort queued transfer on buffer 1 - usb_hw->abort |= TU_BIT(ep_id); - - while ( !(usb_hw->abort_done & TU_BIT(ep_id)) ) {} - - uint32_t ep_ctrl = *ep->endpoint_control; - ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); - ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; - - io_rw_32 *buf_ctrl_reg = is_host ? hwbuf_ctrl_reg_host(ep) : hwbuf_ctrl_reg_device(ep); - hwbuf_ctrl_set(buf_ctrl_reg, 0); - - usb_hw->abort &= ~TU_BIT(ep_id); - - TU_LOG(3, "----SHORT PACKET buffer0 on EP %02X:\r\n", ep->ep_addr); - TU_LOG(3, " BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl)); -#endif - } - } -} - -// Returns true if transfer is complete -bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { +// Returns true if transfer is complete. +// buf_id: which buffer completed (from BUFF_CPU_SHOULD_HANDLE, only used for double-buffered). +bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, + io_rw_32 *buf_reg, uint8_t buf_id) { hw_endpoint_lock_update(ep, 1); - // Part way through a transfer if (!ep->active) { panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); } - sync_xfer(ep, ep_reg, buf_reg); // Update EP struct from hardware state + const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); + const bool is_host = rp2usb_is_host_mode(); + const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); + const bool is_double = ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS); + + const uint16_t xferred = hwbuf_sync(ep, buf_reg, is_double ? buf_id : 0, is_rx); + bool is_done = (ep->remaining_len == 0); + + if (is_double) { + if (xferred < ep->max_packet_size) { + // Short packet + is_done = true; + } else if (buf_id == 0) { + // buf0 done: wait for buf1, don't start new buffers + hw_endpoint_lock_update(ep, -1); + return false; + } + // buf1 done: is_done determined by remaining_len above + } - // Now we have synced our state with the hardware. Is there more data to transfer? - // If we are done then notify tinyusb - if (ep->remaining_len == 0) { - pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr); - // Notify caller we are done so it can notify the tinyusb stack - hw_endpoint_lock_update(ep, -1); - return true; - } else { + if (!is_done) { #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX if (e15_is_critical_frame_period(ep)) { ep->pending = 1; } else #endif { - hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg); + hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); } } hw_endpoint_lock_update(ep, -1); - // More work to do - return false; + return is_done; } //--------------------------------------------------------------------+ diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 46bd727c8..c4dd0cb98 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -26,6 +26,8 @@ #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX #endif + + #define CFG_TUSB_RP2040_ERRATA_E4_FIX 1 #endif #ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX @@ -45,6 +47,10 @@ #define PICO_RP2040_USB_FAST_IRQ 0 #endif +#ifndef CFG_TUSB_RP2040_ERRATA_E4_FIX +#define CFG_TUSB_RP2040_ERRATA_E4_FIX 0 +#endif + #if PICO_RP2040_USB_FAST_IRQ #define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x) #else @@ -108,8 +114,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { //--------------------------------------------------------------------+ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); -bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); -void hw_endpoint_start_next_buffer(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); +bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id); +void hw_endpoint_buffer_xact(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); void hw_endpoint_reset_transfer(struct hw_endpoint *ep); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { -- cgit v1.3.1 From aeac28e5171442c6aef5ec44f9043d5e74371e41 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 25 Mar 2026 11:23:50 +0700 Subject: update hcd to handle interrupt per buf --- hw/bsp/rp2040/family.c | 6 +- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 7 +-- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 89 ++++++++++++++-------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 29 +++++---- test/hil/hil_test.py | 57 +++++++++++++++--- 5 files changed, 116 insertions(+), 72 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index adb58449d..c64025036 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -165,7 +165,11 @@ void board_init(void) { #if (CFG_TUH_ENABLED && CFG_TUH_RPI_PIO_USB) || (CFG_TUD_ENABLED && CFG_TUD_RPI_PIO_USB) // Set the system clock to a multiple of 12mhz for bit-banging USB with pico-usb - set_sys_clock_khz(120000, true); + #if defined(PICO_RP2350) && PICO_RP2350 == 1 + set_sys_clock_khz(156000, true); // rp2350 default is 150Mhz + #else + set_sys_clock_khz(120000, true); // rp2040 default is 125Mhz + #endif // set_sys_clock_khz(180000, true); // set_sys_clock_khz(192000, true); // set_sys_clock_khz(240000, true); diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index ec73cafc1..0665484a0 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -69,7 +69,7 @@ TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8 return hw_endpoint_get(num, dir); } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_ep_ctrl(const uint8_t epnum, tusb_dir_t dir) { +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_ep_ctrl(uint8_t epnum, tusb_dir_t dir) { if (epnum == 0) { // EP0 has no endpoint control register because the buffer offsets are fixed and always enabled return NULL; @@ -78,7 +78,7 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_ep_ctrl(const uint8_t epnum, t return (dir == TUSB_DIR_IN) ? &ep_ctrl->in : &ep_ctrl->out; } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_buf_ctrl(const uint8_t epnum, tusb_dir_t dir) { +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_buf_ctrl(uint8_t epnum, tusb_dir_t dir) { struct usb_device_dpram_ep_buf_ctrl *buf_ctrl = &usb_dpram->ep_buf_ctrl[epnum]; return (dir == TUSB_DIR_IN) ? &buf_ctrl->in : &buf_ctrl->out; } @@ -182,6 +182,7 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { // Read which buffer to handle BEFORE clearing buf_status uint8_t buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; usb_hw_clear->buf_status = bit; + buf_status &= ~bit; // IN transfer for even i, OUT transfer for odd i const uint8_t epnum = i >> 1u; @@ -205,8 +206,6 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { hw_endpoint_reset_transfer(ep); dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); } - - buf_status &= ~bit; } } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 9ee15d343..4ebdf8284 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -94,7 +94,13 @@ static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { return NULL; } -// static hw_endpoint_t* epdt_find_interrupt(uint8_t ) +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *dpram_int_ep_ctrl(uint8_t int_num) { + return &usbh_dpram->int_ep_ctrl[int_num-1].ctrl; +} + +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 * dpram_int_ep_buffer_ctrl(uint8_t int_num) { + return &usbh_dpram->int_ep_buffer_ctrl[int_num-1].ctrl; +} //--------------------------------------------------------------------+ // @@ -129,57 +135,53 @@ static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_resul } } -static void __tusb_irq_path_func(handle_hwbuf_status_bit)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { - const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg); - if (done) { - hw_xfer_complete(ep, XFER_RESULT_SUCCESS); - } -} - static void __tusb_irq_path_func(handle_hwbuf_status)(void) { - uint32_t buf_status = usb_hw->buf_status; pico_trace("buf_status 0x%08lx\n", buf_status); + enum { + BUF_STATUS_EPX = 1u + }; - // Check EPX first - uint32_t bit = 1u; - if (buf_status & bit) { - buf_status &= ~bit; - usb_hw_clear->buf_status = bit; + // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0. + // Double-buffered: if both buffers completed at once, buf_status re-sets + // immediately after clearing (datasheet Table 406). Process the second buffer too. + while (usb_hw->buf_status & BUF_STATUS_EPX) { + const uint8_t buf_id = (usb_hw->buf_cpu_should_handle & BUF_STATUS_EPX) ? 1 : 0; + usb_hw_clear->buf_status = 1u; // clear io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - handle_hwbuf_status_bit(epx, ep_reg, buf_reg); + if (hw_endpoint_xfer_continue(epx, ep_reg, buf_reg, buf_id)) { + hw_xfer_complete(epx, XFER_RESULT_SUCCESS); + } } // Check "interrupt" (asynchronous) endpoints for both IN and OUT - // TODO use clz for better efficiency - for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && buf_status; i++) { - // EPX IN/OUT is bit 0, 1 + uint32_t buf_status = usb_hw->buf_status & ~1u; + while (buf_status) { + // ctz/clz is faster than loop which has only a few bit set in general + const uint8_t idx = (uint8_t) __builtin_ctz(buf_status); + const uint bit = TU_BIT(idx); + usb_hw_clear->buf_status = bit; + buf_status &= ~bit; + + // IN transfer for even i, OUT transfer for odd i + // EPX is bit 0. Bit 1 is not used // IEP1 IN/OUT is bit 2, 3 - // IEP2 IN/OUT is bit 4, 5 - // etc - for (uint j = 0; j < 2; j++) { - bit = 1 << (i * 2 + j); - if (buf_status & bit) { - buf_status &= ~bit; - usb_hw_clear->buf_status = bit; - - for (uint8_t e = 0; e < USB_MAX_ENDPOINTS; e++) { - hw_endpoint_t *ep = &ep_pool[e]; - if (ep->interrupt_num == i) { - io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; - io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; - handle_hwbuf_status_bit(ep, ep_reg, buf_reg); - break; - } + // IEP2 IN/OUT is bit 4, 5 etc + const uint8_t epnum = idx >> 1u; + for (size_t e = 0; e < TU_ARRAY_SIZE(ep_pool); e++) { + hw_endpoint_t *ep = &ep_pool[e]; + if (ep->interrupt_num == epnum) { + io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); + io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); + const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, 0); + if (done) { + hw_xfer_complete(ep, XFER_RESULT_SUCCESS); } + break; } } } - - if (buf_status) { - panic("Unhandled buffer %d\n", buf_status); - } } // All non-interrupt endpoints use shared EPX. @@ -491,8 +493,10 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; usb_hw->int_ep_addr_ctrl[ep->interrupt_num - 1] = 0; - usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl = 0; - usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl = 0; + io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); + io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); + *buf_reg = 0; + *ep_reg = 0; } ep->max_packet_size = 0; // mark as unused @@ -547,13 +551,12 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; } -// xfer using epx static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { if (ep->transfer_type == TUSB_XFER_INTERRUPT) { // For interrupt endpoint control and buffer is already configured // Note: Interrupt is single buffered only - io_rw_32 *ep_reg = &usbh_dpram->int_ep_ctrl[ep->interrupt_num - 1].ctrl; - io_rw_32 *buf_reg = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num - 1].ctrl; + io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); + io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); } else { const uint8_t ep_num = tu_edpt_number(ep->ep_addr); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index ac1536f5b..1e2c4f211 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -124,8 +124,10 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, // wait for USB controller to see the update before setting AVAILABLE. - // Host also needs this for continuation buffers in multi-packet transfers. - busy_wait_at_least_cycles(12); + // Don't need delay in host mode as host is in charge of when to start the transaction. + if (!is_host) { + busy_wait_at_least_cycles(12); + } } } @@ -193,13 +195,13 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406). uint32_t ep_ctrl = *ep_reg | EP_CTRL_INTERRUPT_PER_BUFFER; - // Since short packet on buf0 in double-buffered RX: buf1 may already contain data from the - // NEXT transfer (host sent it before CPU processed this IRQ). Cannot safely recover. Avoid by not using double - // buffering for rx transfer - bool force_single = is_rx; - #if CFG_TUH_ENABLED - force_single |= (is_host && ep->interrupt_num != 0); // host interrupt is single only - #endif + // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause + // short packet on buffer0 + // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single + // buffered, + // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint + const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0); + // bool force_single = is_rx || (is_host && ep->interrupt_num != 0); if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data @@ -318,14 +320,11 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); const bool is_double = ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS); - const uint16_t xferred = hwbuf_sync(ep, buf_reg, is_double ? buf_id : 0, is_rx); - bool is_done = (ep->remaining_len == 0); + hwbuf_sync(ep, buf_reg, buf_id, is_rx); + const bool is_done = (ep->remaining_len == 0); if (is_double) { - if (xferred < ep->max_packet_size) { - // Short packet - is_done = true; - } else if (buf_id == 0) { + if (buf_id == 0) { // buf0 done: wait for buf1, don't start new buffers hw_endpoint_lock_update(ep, -1); return false; diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 4b17bed54..8b0070d40 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -155,8 +155,7 @@ def read_disk_file(uid, lun, fname): def open_mtp_dev(uid): mtp = MTP() - # MTP seems to take a while to enumerate - timeout = 2 * ENUM_TIMEOUT + timeout = ENUM_TIMEOUT while timeout > 0: # unmount gio/gvfs MTP mount which blocks libmtp from accessing the device subprocess.run(f"gio mount -u mtp://TinyUsb_TinyUsb_Device_{uid}/", @@ -607,7 +606,7 @@ def test_host_msc_file_explorer(board): lines = data.decode('utf-8', errors='ignore').splitlines() check_msc_info(lines, msc_devs) - # Send "cat README.TXT" and read response + # Send "cat README.TXT" and check response (optional — file may not exist on all drives) time.sleep(1) ser.reset_input_buffer() for ch in 'cat README.TXT\r': @@ -615,24 +614,20 @@ def test_host_msc_file_explorer(board): ser.flush() time.sleep(0.002) - # Read response resp = b'' t = 10.0 while t > 0: rd = ser.read(max(1, ser.in_waiting)) if rd: resp += rd - # wait for prompt after command output if b'>' in resp and resp.rstrip().endswith(b'>'): break time.sleep(0.05) t -= 0.05 - # Verify response contains README content resp_text = resp.decode('utf-8', errors='ignore') - assert MSC_README_TXT.decode() in resp_text, (f'MSC README.TXT not found in response:\n' - f' received: {resp_text}') - print('README.TXT matched ', end='') + if MSC_README_TXT.decode() in resp_text: + print('README.TXT matched ', end='') # MSC throughput test: send dd command to read sectors time.sleep(0.5) @@ -740,6 +735,50 @@ 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 + 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' + + block_count = 16 + block_size = 512 + tmp_file = f'/tmp/msc_dd_{uid}.bin' + + # 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()}' + dd_out = ret.stdout.decode() + read_speed = '' + for line in dd_out.splitlines(): + m = re.search(r'(\d+[\.\d]*\s+[kMG]?B/s)', line) + if m: + read_speed = m.group(1) + break + + # Write back the same data to avoid corrupting the disk + ret = run_cmd(f'dd if={tmp_file} of={dev} bs={block_size} count={block_count} oflag=direct 2>&1') + assert ret.returncode == 0, f'dd write failed: {ret.stdout.decode()}' + dd_out = ret.stdout.decode() + write_speed = '' + for line in dd_out.splitlines(): + m = re.search(r'(\d+[\.\d]*\s+[kMG]?B/s)', line) + if m: + write_speed = m.group(1) + break + + 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) -- cgit v1.3.1 From e81faa22af6369745e4aa0420289a335d2eaa047 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 25 Mar 2026 15:56:09 +0700 Subject: fix e4 incorrect buf with incorrect buf_id = 1 --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 2 +- src/portable/raspberrypi/rp2040/rp2040_usb.c | 85 +++++++++++++++++++--------- 2 files changed, 58 insertions(+), 29 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 0665484a0..65871df05 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -368,7 +368,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { (void) rh_init; assert(rhport == 0); - TU_LOG(1, "Chip Version B%u\r\n", rp2040_chip_version()); + // TU_LOG(1, "Chip Version B%u\r\n", rp2040_chip_version()); // Reset hardware to default state rp2usb_init(); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 1e2c4f211..1d21952a8 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -135,7 +135,7 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an } // prepare buffer, move data if tx, return buffer control -static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) { +static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); ep->remaining_len -= buflen; @@ -148,15 +148,14 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint if (!is_rx) { if (buflen) { // Copy data from user buffer/fifo to hw buffer - uint8_t *hw_buf = ep->dpram_buf + (buf_id << 6); #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround - tu_hwfifo_write_from_fifo(hw_buf, ep->user_fifo, buflen, NULL); + tu_hwfifo_write_from_fifo(dpram_buf, ep->user_fifo, buflen, NULL); } else #endif { - unaligned_memcpy(hw_buf, ep->user_buf, buflen); + unaligned_memcpy(dpram_buf, ep->user_buf, buflen); ep->user_buf += buflen; } } @@ -186,27 +185,40 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw is_rx = (dir == TUSB_DIR_OUT); } + // In case short packet on buf0 in double-buffered RX, buf1 may already contain data from the + // NEXT transfer (host sent it before CPU processed this IRQ). Cannot safely recover. Avoid by not using double + // buffering for rx transfer + + // RP2040-E4 (host only): in single-buffered multi-packet transfers, the controller may write completion status to + // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 + // since it leave BUF0 intact, which can be polled before buf_status interrupt is trigger. + // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. + #if CFG_TUSB_RP2040_ERRATA_E4_FIX + + #endif + // always compute and start with buffer 0 - uint32_t buf_ctrl = hwbuf_prepare(ep, 0, is_rx) | USB_BUF_CTRL_SEL; + uint32_t buf_ctrl = hwbuf_prepare(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; // Device mode EP0 has no endpoint control register if (ep_reg != NULL) { // Each buffer completion triggers its own IRQ. // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406). uint32_t ep_ctrl = *ep_reg | EP_CTRL_INTERRUPT_PER_BUFFER; - - // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause - // short packet on buffer0 - // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single - // buffered, - // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint +#if 1 const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0); - // bool force_single = is_rx || (is_host && ep->interrupt_num != 0); +#else + bool force_single = false; // is_rx; + #if CFG_TUH_ENABLED + if (is_host && ep->interrupt_num != 0) { + force_single = true; + } + #endif +#endif if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data - // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) - buf_ctrl |= (hwbuf_prepare(ep, 1, is_rx) << 16); + buf_ctrl |= hwbuf_prepare(ep, ep->dpram_buf+64, is_rx) << 16; ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS; } else { // Single buffered since 1 is enough @@ -216,6 +228,8 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw *ep_reg = ep_ctrl; } + // TU_LOG(1, "xact: buf_ctrl = 0x%08lx\r\n", buf_ctrl); + // Finally, write to buffer_control which will trigger the transfer the next time the controller polls this endpoint hwbuf_ctrl_set(buf_reg, buf_ctrl); } @@ -264,13 +278,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *buf_ctrl_reg, uint8_t buf_id, - bool is_rx) { - uint32_t buf_ctrl = *buf_ctrl_reg; - if (buf_id) { - buf_ctrl = buf_ctrl >> 16; - } - +static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, uint32_t buf_ctrl, uint8_t *dpram_buf) { const uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; if (!is_rx) { @@ -281,16 +289,14 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *bu // If we have received some data, so can increase the length // we have received AFTER we have copied it to the user buffer at the appropriate offset assert(buf_ctrl & USB_BUF_CTRL_FULL); - - uint8_t *hw_buf = ep->dpram_buf + buf_id * 64; #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround - tu_hwfifo_read_to_fifo(hw_buf, ep->user_fifo, xferred_bytes, NULL); + tu_hwfifo_read_to_fifo(dpram_buf, ep->user_fifo, xferred_bytes, NULL); } else #endif { - unaligned_memcpy(ep->user_buf, hw_buf, xferred_bytes); + unaligned_memcpy(ep->user_buf, dpram_buf, xferred_bytes); ep->user_buf += xferred_bytes; } } @@ -307,8 +313,7 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *bu // Returns true if transfer is complete. // buf_id: which buffer completed (from BUFF_CPU_SHOULD_HANDLE, only used for double-buffered). -bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, - io_rw_32 *buf_reg, uint8_t buf_id) { +bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id) { hw_endpoint_lock_update(ep, 1); if (!ep->active) { @@ -320,7 +325,31 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); const bool is_double = ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS); - hwbuf_sync(ep, buf_reg, buf_id, is_rx); + #if CFG_TUSB_RP2040_ERRATA_E4_FIX + const bool need_e4_fix = (is_host && !is_double); + #endif + + // Double-buffered: buf_id from BUFF_CPU_SHOULD_HANDLE indicates which buffer completed. + + // RP2040-E4 (host only): in single-buffered multi-packet transfers, the controller may write completion status to + // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 + // since it leave BUF0 intact, which can be poll before buf_status interrupt is trigger. + // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. + uint32_t buf_ctrl = *buf_reg; + // TU_LOG(1, "sync: buf_ctrl = 0x%08lx, buf id = %u\r\n", buf_ctrl, buf_id); + + uint8_t* dpram_buf = ep->dpram_buf; + if (buf_id) { + buf_ctrl = buf_ctrl >> 16; + #if CFG_TUSB_RP2040_ERRATA_E4_FIX + if (!need_e4_fix) // incorrect buf_id, buffer pointer is still buf0 + #endif + { + dpram_buf += 64; // buf1 offset + } + } + + hwbuf_sync(ep, is_rx, buf_ctrl, dpram_buf); const bool is_done = (ep->remaining_len == 0); if (is_double) { -- cgit v1.3.1 From 94f48272c79310c5ab47c79c7b57a1be7bdee901 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 26 Mar 2026 19:33:08 +0700 Subject: implement ping-pong double buffered for both tx and rx --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 77 +++++---- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 13 +- src/portable/raspberrypi/rp2040/rp2040_usb.c | 223 ++++++++++++++++++++------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 65 +++++--- test/hil/hil_test.py | 2 +- 5 files changed, 267 insertions(+), 113 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 65871df05..d9c30efba 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -92,12 +92,13 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa // Clear existing buffer control state const uint8_t epnum = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); - io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); - *buf_ctrl_reg = 0; + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + + *buf_reg = 0; // allocated hw buffer if (epnum == 0) { - // Buffer offset is fixed (also double buffered) + // Buffer offset is fixed (also double buffered) TODO EP0 double buffer ep->dpram_buf = (uint8_t *)&usb_dpram->ep0_buf_a[0]; } else { // round up size to multiple of 64 @@ -107,7 +108,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa if (transfer_type == TUSB_XFER_BULK) { size *= 2u; - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #if CFG_TUSB_RP2_ERRATA_E15 if (dir == TUSB_DIR_IN) { ep->e15_bulk_in = true; } @@ -127,8 +128,8 @@ static void hw_endpoint_enable(uint8_t epnum, tusb_dir_t dir, uint8_t transfer_t io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); // Set endpoint control register to enable (EP0 has no endpoint control register) if (ep_reg != NULL) { - const uint32_t ctrl_value = - EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(dpram_buf); + const uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(dpram_buf); *ep_reg = ctrl_value; } } @@ -179,32 +180,25 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { const uint8_t i = (uint8_t) __builtin_ctz(buf_status); const uint bit = TU_BIT(i); - // Read which buffer to handle BEFORE clearing buf_status - uint8_t buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; - usb_hw_clear->buf_status = bit; - buf_status &= ~bit; - // IN transfer for even i, OUT transfer for odd i - const uint8_t epnum = i >> 1u; - const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; - hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - - io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); - io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); - bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id); + const uint8_t epnum = i >> 1u; + const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN; + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); // Double-buffered: if both buffers completed at once, buf_status re-sets // immediately after clearing (datasheet Table 406). Process the second buffer too. - if (!done && (usb_hw->buf_status & bit)) { - buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; + while (usb_hw->buf_status & bit) { + const uint8_t buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0; // before clear buf_status usb_hw_clear->buf_status = bit; - done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id); - } + buf_status &= ~bit; - if (done) { - const uint16_t xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); - dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); + if (hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id)) { + const uint16_t xferred_len = ep->xferred_len; + hw_endpoint_reset_transfer(ep); + dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); + } } } } @@ -244,7 +238,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { handled |= USB_INTF_DEV_SOF_BITS; -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +#if CFG_TUSB_RP2_ERRATA_E15 // Errata 15 workaround for Device Bulk-In endpoint e15_last_sof = time_us_32(); @@ -260,7 +254,32 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { ep->pending = 0; io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); io_rw_32 *buf_reg = get_buf_ctrl(i, TUSB_DIR_IN); - hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); + io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg; + + // Check each buffer half: idle when both FULL and AVAIL are clear. + // Use 16-bit writes to avoid clobbering the other half (DPSRAM concurrent access). + const uint16_t busy_mask = USB_BUF_CTRL_FULL | USB_BUF_CTRL_AVAIL; + const bool do_buf0 = !(buf_reg16[0] & busy_mask); + const bool do_buf1 = ep->remaining_len > 0 && !(buf_reg16[1] & busy_mask); + + // Set ep_ctrl BEFORE buf_ctrl (controller reads ep_ctrl to determine double-buffered mode) + if (ep_reg != NULL) { + if (do_buf1) { + *ep_reg |= EP_CTRL_DOUBLE_BUFFERED_BITS; + } else { + *ep_reg &= ~EP_CTRL_DOUBLE_BUFFERED_BITS; + } + } + + if (do_buf0) { + uint16_t buf0 = bufctrl_prepare(ep, ep->dpram_buf, false); + buf0 |= USB_BUF_CTRL_SEL; // reset buffer selector to buf0 + bufctrl_write16(buf_reg16, buf0); + } + if (do_buf1) { + uint16_t buf1 = bufctrl_prepare(ep, ep->dpram_buf + 64, false); + bufctrl_write16(buf_reg16 + 1, buf1); + } } hw_endpoint_lock_update(ep, -1); } @@ -464,7 +483,7 @@ void dcd_sof_enable(uint8_t rhport, bool en) { if (en) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; } -#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #if !CFG_TUSB_RP2_ERRATA_E15 else { // Don't clear immediately if the SOF workaround is in use. // The SOF handler will conditionally disable the interrupt. diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 4ebdf8284..3f4c95422 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -351,11 +351,11 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_des // const uint8_t bmInterval = ep_desc->bInterval; ep->max_packet_size = wMaxPacketSize; - ep->ep_addr = ep_addr; - ep->dev_addr = dev_addr; - ep->transfer_type = transfer_type; - ep->need_pre = need_pre(dev_addr); - ep->next_pid = 0u; + ep->ep_addr = ep_addr; + ep->dev_addr = dev_addr; + ep->transfer_type = transfer_type; + ep->need_pre = need_pre(dev_addr); + ep->next_pid = 0u; if (transfer_type != TUSB_XFER_INTERRUPT) { ep->dpram_buf = usbh_dpram->epx_data; @@ -572,8 +572,7 @@ static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_ // ep control const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset /*| - (1u << 16)*/; // INTERRUPT_ON_NAK + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; usbh_dpram->epx_ctrl = ep_ctrl; io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 1d21952a8..d0a229785 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -27,18 +27,23 @@ #include "tusb_option.h" -#if CFG_TUSB_MCU == OPT_MCU_RP2040 +#if CFG_TUSB_MCU == OPT_MCU_RP2040 && (CFG_TUD_ENABLED || CFG_TUH_ENABLED) -#include -#include "rp2040_usb.h" + #include + #include "rp2040_usb.h" + + #include "device/dcd.h" + #include "host/hcd.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #if CFG_TUSB_RP2_ERRATA_E15 static bool e15_is_critical_frame_period(struct hw_endpoint *ep); - #else - #define e15_is_critical_frame_period(x) (false) + #endif + + #if CFG_TUSB_RP2_ERRATA_E2 +static uint8_t rp2040_chipversion = 2; #endif //--------------------------------------------------------------------+ @@ -84,6 +89,10 @@ void rp2usb_init(void) { // Mux the controller to the onboard usb phy usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; + #if CFG_TUSB_RP2_ERRATA_E2 + rp2040_chipversion = rp2040_chip_version(); + #endif + TU_LOG2_INT(sizeof(hw_endpoint_t)); } @@ -134,12 +143,46 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an *buf_ctrl_reg = value; } +void __tusb_irq_path_func(bufctrl_write32)(io_rw_32 *buf_reg, uint32_t value) { + const uint32_t current = *buf_reg; + const uint32_t avail_mask = USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16); + if (current & value & avail_mask) { + panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_reg); + } + *buf_reg = value & ~USB_BUF_CTRL_AVAIL; // write other bits first + + // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, + // wait for USB controller to see the update before setting AVAILABLE. + // Don't need delay in host mode as host is in charge of when to start the transaction. + if (!rp2usb_is_host_mode() && (value & (USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16)))) { + busy_wait_at_least_cycles(12); + } + + *buf_reg = value; // then set AVAILABLE bit (if set) last +} + +void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) { + const uint16_t current = *buf_reg16; + if (current & value & USB_BUF_CTRL_AVAIL) { + panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_reg16); + } + *buf_reg16 = value & (uint16_t)~USB_BUF_CTRL_AVAIL; // write other bits first + + // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, + // wait for USB controller to see the update before setting AVAILABLE. + // Don't need delay in host mode as host is in charge of when to start the transaction. + if (!rp2usb_is_host_mode() && (value & USB_BUF_CTRL_AVAIL)) { + busy_wait_at_least_cycles(12); + } + *buf_reg16 = value; // then set AVAILABLE bit (if set) last +} + // prepare buffer, move data if tx, return buffer control -static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { +uint16_t __tusb_irq_path_func(bufctrl_prepare)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); ep->remaining_len -= buflen; - uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; + uint16_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; if (ep->next_pid) { buf_ctrl |= USB_BUF_CTRL_DATA1_PID; } @@ -174,8 +217,8 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint } // Start transaction on hw buffer -void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { - const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); +void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { + const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); const bool is_host = rp2usb_is_host_mode(); bool is_rx; @@ -185,29 +228,26 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw is_rx = (dir == TUSB_DIR_OUT); } - // In case short packet on buf0 in double-buffered RX, buf1 may already contain data from the - // NEXT transfer (host sent it before CPU processed this IRQ). Cannot safely recover. Avoid by not using double - // buffering for rx transfer - // RP2040-E4 (host only): in single-buffered multi-packet transfers, the controller may write completion status to // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 // since it leave BUF0 intact, which can be polled before buf_status interrupt is trigger. // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. - #if CFG_TUSB_RP2040_ERRATA_E4_FIX + #if CFG_TUSB_RP2_ERRATA_E4 #endif // always compute and start with buffer 0 - uint32_t buf_ctrl = hwbuf_prepare(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; + uint32_t buf_ctrl = bufctrl_prepare(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; // Device mode EP0 has no endpoint control register if (ep_reg != NULL) { // Each buffer completion triggers its own IRQ. // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406). - uint32_t ep_ctrl = *ep_reg | EP_CTRL_INTERRUPT_PER_BUFFER; -#if 1 - const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0); -#else + uint32_t ep_ctrl = *ep_reg; + #if 1 + const bool force_single = // (!is_host && is_rx) || + (is_host && tu_edpt_number(ep->ep_addr) != 0); + #else bool force_single = false; // is_rx; #if CFG_TUH_ENABLED if (is_host && ep->interrupt_num != 0) { @@ -218,7 +258,7 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data - buf_ctrl |= hwbuf_prepare(ep, ep->dpram_buf+64, is_rx) << 16; + buf_ctrl |= (uint32_t)bufctrl_prepare(ep, ep->dpram_buf + 64, is_rx) << 16; ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS; } else { // Single buffered since 1 is enough @@ -228,10 +268,8 @@ void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw *ep_reg = ep_ctrl; } - // TU_LOG(1, "xact: buf_ctrl = 0x%08lx\r\n", buf_ctrl); - // Finally, write to buffer_control which will trigger the transfer the next time the controller polls this endpoint - hwbuf_ctrl_set(buf_reg, buf_ctrl); + bufctrl_write32(buf_reg, buf_ctrl); } void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, @@ -261,7 +299,41 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * ep->is_xfer_fifo = false; } - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + if (ep->future_len > 0) { + // only on rx endpoint + const uint8_t future_len = ep->future_len; + memcpy(ep->user_buf, ep->dpram_buf + (ep->future_bufid << 6), future_len); + ep->xferred_len += future_len; + ep->remaining_len -= future_len; + ep->user_buf += future_len; + + ep->future_len = 0; + ep->future_bufid = 0; + + if (ep->remaining_len == 0) { + // all data has been received, no need to start hw transfer + ep->active = false; + const uint16_t xferred_len = ep->xferred_len; + hw_endpoint_reset_transfer(ep); + + const bool is_host = rp2usb_is_host_mode(); + #if CFG_TUH_ENABLED + if (is_host) { + hcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); + } + #endif + #if CFG_TUD_ENABLED + if (!is_host) { + dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); + } + #endif + + hw_endpoint_lock_update(ep, -1); + return; + } + } + + #if CFG_TUSB_RP2_ERRATA_E15 if (ep->e15_bulk_in) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; } @@ -271,14 +343,14 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * } else #endif { - hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); + hw_endpoint_buffer_start(ep, ep_reg, buf_reg); } hw_endpoint_lock_update(ep, -1); } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, uint32_t buf_ctrl, uint8_t *dpram_buf) { +static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, uint16_t buf_ctrl, uint8_t *dpram_buf) { const uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; if (!is_rx) { @@ -316,16 +388,23 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id) { hw_endpoint_lock_update(ep, 1); + const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); + const bool is_host = rp2usb_is_host_mode(); + const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); + + io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg; + uint16_t buf_ctrl16 = *(buf_reg16 + buf_id); + if (!ep->active) { - panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); + // probably land here due to short packet on rx with double buffered + hw_endpoint_lock_update(ep, -1); + return false; } - const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - const bool is_host = rp2usb_is_host_mode(); - const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); - const bool is_double = ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS); + const bool is_double = (ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS)); + (void)is_double; - #if CFG_TUSB_RP2040_ERRATA_E4_FIX + #if CFG_TUSB_RP2_ERRATA_E4 const bool need_e4_fix = (is_host && !is_double); #endif @@ -335,40 +414,73 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 // since it leave BUF0 intact, which can be poll before buf_status interrupt is trigger. // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. - uint32_t buf_ctrl = *buf_reg; - // TU_LOG(1, "sync: buf_ctrl = 0x%08lx, buf id = %u\r\n", buf_ctrl, buf_id); - uint8_t* dpram_buf = ep->dpram_buf; if (buf_id) { - buf_ctrl = buf_ctrl >> 16; - #if CFG_TUSB_RP2040_ERRATA_E4_FIX - if (!need_e4_fix) // incorrect buf_id, buffer pointer is still buf0 + #if CFG_TUSB_RP2_ERRATA_E4 + if (!need_e4_fix) // incorrect buf_id, buffer pointer is still buf0 #endif { dpram_buf += 64; // buf1 offset } } - hwbuf_sync(ep, is_rx, buf_ctrl, dpram_buf); - const bool is_done = (ep->remaining_len == 0); + const uint16_t xact_bytes = hwbuf_sync(ep, is_rx, buf_ctrl16, dpram_buf); + const bool is_last = buf_ctrl16 & USB_BUF_CTRL_LAST; + const bool is_short = xact_bytes < ep->max_packet_size; + const bool is_done = is_short || (buf_ctrl16 & USB_BUF_CTRL_LAST); + + // short packet on rx with double buffer: abort the other half (if not last) and reset double-buffer state. + // The other buffer may be: (a) still AVAIL, (b) in-progress (controller receiving), or (c) already completed. + // We must abort to safely reclaim it. If it has valid data (FULL), save as future for the next transfer. + // After abort, zero buf_ctrl + if (is_short && is_double && is_rx && !is_last) { + io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); + const uint32_t abort_bit = TU_BIT((tu_edpt_number(ep->ep_addr) << 1) | (dir ? 0 : 1)); + + #if CFG_TUSB_RP2_ERRATA_E2 + if (rp2040_chipversion >= 2) + #endif + { + usb_hw_set->abort = abort_bit; + while ((usb_hw->abort_done & abort_bit) != abort_bit) {} + } - if (is_double) { - if (buf_id == 0) { - // buf0 done: wait for buf1, don't start new buffers - hw_endpoint_lock_update(ep, -1); - return false; + // After abort, check if the other buffer received valid data + const uint16_t buf_ctrl16_other = *buf_reg16_other; + if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { + // Host already sent data into this buffer (e.g. write payload right after short CBW). + // Save it for the next transfer. + ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); + ep->future_bufid = buf_id ^ 1; + // buff_status will be clear by the next run + } else { + ep->next_pid ^= 1u; + } + + *buf_reg = 0; // reset buffer control + + #if CFG_TUSB_RP2_ERRATA_E2 + if (rp2040_chipversion >= 2) + #endif + { + usb_hw_clear->abort_done = abort_bit; + usb_hw_clear->abort = abort_bit; } - // buf1 done: is_done determined by remaining_len above + + hw_endpoint_lock_update(ep, -1); + return true; } - if (!is_done) { - #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + if (!is_done && ep->remaining_len > 0) { + #if CFG_TUSB_RP2_ERRATA_E15 if (e15_is_critical_frame_period(ep)) { ep->pending = 1; } else #endif { - hw_endpoint_buffer_xact(ep, ep_reg, buf_reg); + // ping-pong: do 16-bit write since controller is accessing the other half + const uint16_t buf_ctrl16_new = bufctrl_prepare(ep, dpram_buf, is_rx); + bufctrl_write16(buf_reg16 + buf_id, buf_ctrl16_new); } } @@ -380,7 +492,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ // Errata 15 //--------------------------------------------------------------------+ -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +#if CFG_TUSB_RP2_ERRATA_E15 // E15 is fixed with RP2350 /* Don't mark IN buffers as available during the last 200us of a full-speed @@ -410,16 +522,15 @@ static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoin /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point. * The device state machine cannot recover from receiving an incorrect PID - * when it is expecting an ACK. - */ + * when it is expecting an ACK. */ uint32_t delta = time_us_32() - e15_last_sof; if (delta < 800 || delta > 998) { return false; } - TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), - e15_last_sof); + // TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), + // e15_last_sof); return true; } -#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #endif #endif diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index c4dd0cb98..8b0fc83b7 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -15,46 +15,57 @@ #error TinyUSB device and host mode not supported at the same time #endif -// E5 and E15 only apply to RP2040 #if defined(PICO_RP2040) && PICO_RP2040 == 1 - // RP2040 E5: USB device fails to exit RESET state on busy USB bus. + // RP2040-E2 USB device endpoint abort is not cleared. + #define CFG_TUSB_RP2_ERRATA_E2 1 + + // RP2040-E4: USB host writes to upper half of buffer status in single buffered mode. + #define CFG_TUSB_RP2_ERRATA_E4 1 + + // RP2040-E5: USB device fails to exit RESET state on busy USB bus. #if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX) #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX #endif - // RP2040 E15: USB Device controller will hang if certain bus errors occur during an IN transfer. - #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) - #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX + // RP2040-E15: USB Device controller will hang if certain bus errors occur during an IN transfer. + #ifndef CFG_TUSB_RP2_ERRATA_E15 + #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) + #define CFG_TUSB_RP2_ERRATA_E15 PICO_RP2040_USB_DEVICE_UFRAME_FIX + #elif defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) + #define CFG_TUSB_RP2_ERRATA_E15 TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #endif #endif +#endif - #define CFG_TUSB_RP2040_ERRATA_E4_FIX 1 +#ifndef CFG_TUSB_RP2_ERRATA_E2 + #define CFG_TUSB_RP2_ERRATA_E2 0 +#endif + +#ifndef CFG_TUSB_RP2_ERRATA_E4 + #define CFG_TUSB_RP2_ERRATA_E4 0 #endif #ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX #define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX 0 #endif -#ifndef TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX - #define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX 0 +#ifndef CFG_TUSB_RP2_ERRATA_E15 + #define CFG_TUSB_RP2_ERRATA_E15 0 #endif -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +#if CFG_TUSB_RP2_ERRATA_E15 #undef PICO_RP2040_USB_FAST_IRQ #define PICO_RP2040_USB_FAST_IRQ 1 #endif #ifndef PICO_RP2040_USB_FAST_IRQ -#define PICO_RP2040_USB_FAST_IRQ 0 -#endif - -#ifndef CFG_TUSB_RP2040_ERRATA_E4_FIX -#define CFG_TUSB_RP2040_ERRATA_E4_FIX 0 + #define PICO_RP2040_USB_FAST_IRQ 0 #endif #if PICO_RP2040_USB_FAST_IRQ -#define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x) + #define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x) #else -#define __tusb_irq_path_func(x) x + #define __tusb_irq_path_func(x) x #endif //--------------------------------------------------------------------+ @@ -66,6 +77,12 @@ #define pico_info(...) TU_LOG(2, __VA_ARGS__) #define pico_trace(...) TU_LOG(3, __VA_ARGS__) +enum { + EPSTATE_IDLE = 0, + EPSTATE_ACTIVE, + EPSTATE_PENDING, +}; + // Hardware information per endpoint typedef struct hw_endpoint { uint8_t ep_addr; @@ -74,8 +91,11 @@ typedef struct hw_endpoint { uint8_t pending; // Transfer scheduled but not active bool is_xfer_fifo; // transfer using fifo -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX - bool e15_bulk_in; // Errata15 device bulk in + uint8_t future_bufid; + uint8_t future_len; + +#if CFG_TUSB_RP2_ERRATA_E15 + bool e15_bulk_in; // Errata15 device bulk in #endif #if CFG_TUH_ENABLED @@ -98,7 +118,7 @@ typedef struct hw_endpoint { } hw_endpoint_t; -#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX +#if CFG_TUSB_RP2_ERRATA_E15 extern volatile uint32_t e15_last_sof; #endif @@ -115,7 +135,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id); -void hw_endpoint_buffer_xact(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); +void hw_endpoint_buffer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); void hw_endpoint_reset_transfer(struct hw_endpoint *ep); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { @@ -129,6 +149,11 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct //--------------------------------------------------------------------+ void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); +void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value); +void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value); + +uint16_t bufctrl_prepare(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx); + TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { hwbuf_ctrl_update(buf_ctrl_reg, 0, value); } diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 8b0070d40..154a251c1 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -50,7 +50,7 @@ import ctypes from pymtp import MTP import string -ENUM_TIMEOUT = 30 +ENUM_TIMEOUT = 10 STATUS_OK = "\033[32mOK\033[0m" STATUS_FAILED = "\033[31mFailed\033[0m" -- cgit v1.3.1 From 9b3d51790f19676c6863f3362608794eff02bfa2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 26 Mar 2026 22:41:28 +0700 Subject: clean up hw_endpoint_open(), still has issue with E15 and ping-pong (slow read). --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 182 +++++++++++++-------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 58 ++++----- src/portable/raspberrypi/rp2040/rp2040_usb.h | 2 +- test/hil/hil_test.py | 28 ++--- 4 files changed, 126 insertions(+), 144 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index d9c30efba..15ac97dde 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -83,24 +83,28 @@ TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_buf_ctrl(uint8_t epnum, tusb_d return (dir == TUSB_DIR_IN) ? &buf_ctrl->in : &buf_ctrl->out; } -// main processing for dcd_edpt_iso_activate -static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - ep->ep_addr = ep_addr; - ep->next_pid = 0u; - ep->max_packet_size = wMaxPacketSize; - - // Clear existing buffer control state +// Init and enable endpoint +static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, bool ep_enabled) { const uint8_t epnum = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); - io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); - *buf_reg = 0; + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); + ep->ep_addr = ep_addr; + ep->next_pid = 0u; + ep->max_packet_size = wMaxPacketSize; + + // Clear existing buffer control state + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + *buf_reg = 0; // allocated hw buffer if (epnum == 0) { - // Buffer offset is fixed (also double buffered) TODO EP0 double buffer + // Buffer offset is fixed (2 buffer allocated). + // Note: Only single buffer for EP since Double buffered RX can be troublesome with future data. ep->dpram_buf = (uint8_t *)&usb_dpram->ep0_buf_a[0]; } else { + uint32_t ep_ctrl = EP_CTRL_INTERRUPT_PER_BUFFER | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB); + // round up size to multiple of 64 uint16_t size = (uint16_t)tu_round_up(wMaxPacketSize, 64); @@ -119,31 +123,18 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa ep->dpram_buf = hw_buffer_ptr; hw_buffer_ptr += size; + ep_ctrl |= hw_data_offset(ep->dpram_buf); + if (ep_enabled) { + ep_ctrl |= EP_CTRL_ENABLE_BITS; + } + + *get_ep_ctrl(epnum, dir) = ep_ctrl; + hard_assert(hw_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data)); pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->dpram_buf); } } -static void hw_endpoint_enable(uint8_t epnum, tusb_dir_t dir, uint8_t transfer_type, uint8_t *dpram_buf) { - io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); - // Set endpoint control register to enable (EP0 has no endpoint control register) - if (ep_reg != NULL) { - const uint32_t ctrl_value = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(dpram_buf); - *ep_reg = ctrl_value; - } -} - -// Init and enable endpoint -static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { - const uint8_t epnum = tu_edpt_number(ep_addr); - const tusb_dir_t dir = tu_edpt_dir(ep_addr); - hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); - - hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type); - hw_endpoint_enable(epnum, dir, transfer_type, ep->dpram_buf); -} - static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { // Abort any pending transfer const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr); @@ -231,101 +222,101 @@ static void __tusb_irq_path_func(reset_non_control_endpoints)(void) { static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { const uint32_t status = usb_hw->ints; - uint32_t handled = 0; if (status & USB_INTF_DEV_SOF_BITS) { - bool keep_sof_alive = false; + uint32_t sof_count = usb_hw->sof_rd & USB_SOF_RD_BITS; // clear interrupt by reading SOF_RD + + #if CFG_TUSB_RP2_ERRATA_E15 + e15_last_sof = time_us_32(); // timing critical + #endif + + dcd_event_sof(0, sof_count, true); + } + + // xfer events are handled before setup req. So if a transfer completes immediately + // before closing the EP, the events will be delivered in same order. + if (status & USB_INTS_BUFF_STATUS_BITS) { + handle_hw_buff_status(); + } - handled |= USB_INTF_DEV_SOF_BITS; + if (status & USB_INTS_SETUP_REQ_BITS) { + const uint8_t *setup = remove_volatile_cast(const uint8_t *, &usb_dpram->setup_packet); -#if CFG_TUSB_RP2_ERRATA_E15 - // Errata 15 workaround for Device Bulk-In endpoint - e15_last_sof = time_us_32(); + // reset pid to both 1 (data and ack) + reset_ep0(); + // Pass setup packet to tiny usb + dcd_event_setup_received(0, setup, true); + usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS; + } + + // Errata 15 workaround for Device Bulk-In endpoint, must be after BUF_STATUS interrupt to sync buf control first + if (status & USB_INTF_DEV_SOF_BITS) { + bool keep_sof_alive = false; + + #if CFG_TUSB_RP2_ERRATA_E15 for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) { struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF if (ep->e15_bulk_in && ep->active) { keep_sof_alive = true; - hw_endpoint_lock_update(ep, 1); + if (ep->pending) { - ep->pending = 0; - io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); - io_rw_32 *buf_reg = get_buf_ctrl(i, TUSB_DIR_IN); - io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg; + ep->pending = 0; + io_rw_32 *buf_reg32 = (io_rw_32 *)get_buf_ctrl(i, TUSB_DIR_IN); + io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg32; // Check each buffer half: idle when both FULL and AVAIL are clear. // Use 16-bit writes to avoid clobbering the other half (DPSRAM concurrent access). - const uint16_t busy_mask = USB_BUF_CTRL_FULL | USB_BUF_CTRL_AVAIL; - const bool do_buf0 = !(buf_reg16[0] & busy_mask); - const bool do_buf1 = ep->remaining_len > 0 && !(buf_reg16[1] & busy_mask); - - // Set ep_ctrl BEFORE buf_ctrl (controller reads ep_ctrl to determine double-buffered mode) - if (ep_reg != NULL) { - if (do_buf1) { - *ep_reg |= EP_CTRL_DOUBLE_BUFFERED_BITS; - } else { - *ep_reg &= ~EP_CTRL_DOUBLE_BUFFERED_BITS; - } + enum { + BUSY_MASK = USB_BUF_CTRL_FULL | USB_BUF_CTRL_AVAIL + }; + + uint16_t buf0, buf1; + const bool use_buf0 = !(buf_reg16[0] & BUSY_MASK); + + if (use_buf0) { + buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); } - if (do_buf0) { - uint16_t buf0 = bufctrl_prepare(ep, ep->dpram_buf, false); - buf0 |= USB_BUF_CTRL_SEL; // reset buffer selector to buf0 - bufctrl_write16(buf_reg16, buf0); + const bool use_buf1 = (ep->remaining_len > 0) && !(buf_reg16[1] & BUSY_MASK); + if (use_buf1) { + buf1 = bufctrl_prepare16(ep, ep->dpram_buf + 64, false); } - if (do_buf1) { - uint16_t buf1 = bufctrl_prepare(ep, ep->dpram_buf + 64, false); + + if (use_buf0 && use_buf1) { + buf0 |= USB_BUF_CTRL_SEL; // reset to buf0 since order of complete is not guaranteed + bufctrl_write32(buf_reg32, buf0 | ((uint32_t)buf1 << 16)); + } else if (use_buf0) { + bufctrl_write16(buf_reg16, buf0); + } else if (use_buf1) { bufctrl_write16(buf_reg16 + 1, buf1); } } + hw_endpoint_lock_update(ep, -1); } } -#endif + #endif // disable SOF interrupt if it is used for RESUME in remote wakeup if (!keep_sof_alive && !_sof_enable) { usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; } - - dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true); - } - - // xfer events are handled before setup req. So if a transfer completes immediately - // before closing the EP, the events will be delivered in same order. - if (status & USB_INTS_BUFF_STATUS_BITS) { - handled |= USB_INTS_BUFF_STATUS_BITS; - handle_hw_buff_status(); - } - - if (status & USB_INTS_SETUP_REQ_BITS) { - handled |= USB_INTS_SETUP_REQ_BITS; - uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet); - - // reset pid to both 1 (data and ack) - reset_ep0(); - - // Pass setup packet to tiny usb - dcd_event_setup_received(0, setup, true); - usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS; } -#if FORCE_VBUS_DETECT == 0 + #if FORCE_VBUS_DETECT == 0 // Since we force VBUS detect On, device will always think it is connected and // couldn't distinguish between disconnect and suspend if (status & USB_INTS_DEV_CONN_DIS_BITS) { - handled |= USB_INTS_DEV_CONN_DIS_BITS; - if (usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS) { // Connected: nothing to do } else { // Disconnected dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); } - usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS; } #endif @@ -333,9 +324,6 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { // SE0 for 2.5 us or more (will last at least 10ms) if (status & USB_INTS_BUS_RESET_BITS) { pico_trace("BUS RESET\r\n"); - - handled |= USB_INTS_BUS_RESET_BITS; - usb_hw->dev_addr_ctrl = 0; reset_non_control_endpoints(); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); @@ -358,20 +346,15 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { * being disconnected and suspended. */ if (status & USB_INTS_DEV_SUSPEND_BITS) { - handled |= USB_INTS_DEV_SUSPEND_BITS; dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS; } if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) { - handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS; dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS; } - if (status ^ handled) { - panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); - } } /*------------------------------------------------------------------*/ @@ -401,8 +384,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Init control endpoints tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t)); - hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL); - hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL); + hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL, false); + hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL, false); // Init non-control endpoints reset_non_control_endpoints(); @@ -508,7 +491,7 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { (void) rhport; const uint8_t xfer_type = desc_edpt->bmAttributes.xfer; - hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type); + hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type, true); return true; } @@ -516,8 +499,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { // Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void)rhport; - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_init(ep, ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS); + hw_endpoint_open(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS, false); return true; } @@ -534,7 +516,11 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) } ep->max_packet_size = ep_desc->wMaxPacketSize; - hw_endpoint_enable(epnum, dir, TUSB_XFER_ISOCHRONOUS, ep->dpram_buf); + // enable endpoint + io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); + if (ep_reg != NULL) { + *ep_reg |= EP_CTRL_ENABLE_BITS; + } return true; } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index d0a229785..433bd261d 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -39,7 +39,7 @@ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ #if CFG_TUSB_RP2_ERRATA_E15 -static bool e15_is_critical_frame_period(struct hw_endpoint *ep); +static bool e15_is_critical_frame_period(void); #endif #if CFG_TUSB_RP2_ERRATA_E2 @@ -178,7 +178,7 @@ void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) } // prepare buffer, move data if tx, return buffer control -uint16_t __tusb_irq_path_func(bufctrl_prepare)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { +uint16_t __tusb_irq_path_func(bufctrl_prepare16)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); ep->remaining_len -= buflen; @@ -237,16 +237,12 @@ void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_r #endif // always compute and start with buffer 0 - uint32_t buf_ctrl = bufctrl_prepare(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; + uint32_t buf_ctrl = bufctrl_prepare16(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; - // Device mode EP0 has no endpoint control register + // Note: device EP0 does not have an endpoint control register if (ep_reg != NULL) { - // Each buffer completion triggers its own IRQ. - // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406). - uint32_t ep_ctrl = *ep_reg; #if 1 - const bool force_single = // (!is_host && is_rx) || - (is_host && tu_edpt_number(ep->ep_addr) != 0); + const bool force_single = (is_host && tu_edpt_number(ep->ep_addr) != 0); #else bool force_single = false; // is_rx; #if CFG_TUH_ENABLED @@ -256,15 +252,15 @@ void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_r #endif #endif + uint32_t ep_ctrl = *ep_reg; if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data - buf_ctrl |= (uint32_t)bufctrl_prepare(ep, ep->dpram_buf + 64, is_rx) << 16; + buf_ctrl |= (uint32_t)bufctrl_prepare16(ep, ep->dpram_buf + 64, is_rx) << 16; ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS; } else { - // Single buffered since 1 is enough + // Only buf0 used: clear DOUBLE_BUFFERED so controller doesn't toggle buffer selector ep_ctrl &= ~EP_CTRL_DOUBLE_BUFFERED_BITS; } - *ep_reg = ep_ctrl; } @@ -336,16 +332,17 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * #if CFG_TUSB_RP2_ERRATA_E15 if (ep->e15_bulk_in) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; - } - if (e15_is_critical_frame_period(ep)) { - ep->pending = 1; // skip transfer if we are in critical frame period - } else - #endif - { - hw_endpoint_buffer_start(ep, ep_reg, buf_reg); + // skip transfer if we are in critical frame period + if (e15_is_critical_frame_period()) { + ep->pending = 1; + hw_endpoint_lock_update(ep, -1); + return; + } } + #endif + hw_endpoint_buffer_start(ep, ep_reg, buf_reg); hw_endpoint_lock_update(ep, -1); } @@ -454,7 +451,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ ep->future_bufid = buf_id ^ 1; // buff_status will be clear by the next run } else { - ep->next_pid ^= 1u; + ep->next_pid ^= 1u; // roll back pid if aborted } *buf_reg = 0; // reset buffer control @@ -473,13 +470,17 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ if (!is_done && ep->remaining_len > 0) { #if CFG_TUSB_RP2_ERRATA_E15 - if (e15_is_critical_frame_period(ep)) { + if (ep->e15_bulk_in && e15_is_critical_frame_period()) { + // mark as pending if matches E15 condition ep->pending = 1; + } else if (ep->e15_bulk_in && ep->pending) { + // if already pending, meaning the other buf completes first, don't arm buffer, let SOF handle it + // do nothing } else #endif { - // ping-pong: do 16-bit write since controller is accessing the other half - const uint16_t buf_ctrl16_new = bufctrl_prepare(ep, dpram_buf, is_rx); + // ping-pong: arm the completed buffer with new data + const uint16_t buf_ctrl16_new = bufctrl_prepare16(ep, dpram_buf, is_rx); bufctrl_write16(buf_reg16 + buf_id, buf_ctrl16_new); } } @@ -492,7 +493,7 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ // Errata 15 //--------------------------------------------------------------------+ -#if CFG_TUSB_RP2_ERRATA_E15 + #if CFG_TUSB_RP2_ERRATA_E15 // E15 is fixed with RP2350 /* Don't mark IN buffers as available during the last 200us of a full-speed @@ -513,13 +514,8 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_ volatile uint32_t e15_last_sof = 0; -// check if we need to apply Errata 15 workaround : i.e -// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame -static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) { - if (!ep->e15_bulk_in) { - return false; - } - +// check if it is currently in critical frame period i.e 20% of last usb frame +static bool __tusb_irq_path_func(e15_is_critical_frame_period)(void) { /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point. * The device state machine cannot recover from receiving an incorrect PID * when it is expecting an ACK. */ diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 8b0fc83b7..7b79cd2b1 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -152,7 +152,7 @@ void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_ma void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value); void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value); -uint16_t bufctrl_prepare(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx); +uint16_t bufctrl_prepare16(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx); TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { hwbuf_ctrl_update(buf_ctrl_reg, 0, value); diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 154a251c1..04dd4e2bc 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -749,27 +749,27 @@ def test_device_cdc_msc(board): block_size = 512 tmp_file = f'/tmp/msc_dd_{uid}.bin' + # 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 + + 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 '' + # 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()}' - dd_out = ret.stdout.decode() - read_speed = '' - for line in dd_out.splitlines(): - m = re.search(r'(\d+[\.\d]*\s+[kMG]?B/s)', line) - if m: - read_speed = m.group(1) - break + read_speed = parse_dd_speed(ret.stdout.decode()) # Write back the same data to avoid corrupting the disk ret = run_cmd(f'dd if={tmp_file} of={dev} bs={block_size} count={block_count} oflag=direct 2>&1') assert ret.returncode == 0, f'dd write failed: {ret.stdout.decode()}' - dd_out = ret.stdout.decode() - write_speed = '' - for line in dd_out.splitlines(): - m = re.search(r'(\d+[\.\d]*\s+[kMG]?B/s)', line) - if m: - write_speed = m.group(1) - break + write_speed = parse_dd_speed(ret.stdout.decode()) try: os.remove(tmp_file) -- cgit v1.3.1 From dd08939f68cd5e41f45a942efee41dba85110619 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Mar 2026 13:11:53 +0700 Subject: fix E15 workaround issue with out of order in double buffer. device bulk rx/tx ping-pong double buffered all working well --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 29 ++++++++++++---------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 15ac97dde..ea791f4f6 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -265,7 +265,8 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { if (ep->pending) { ep->pending = 0; - io_rw_32 *buf_reg32 = (io_rw_32 *)get_buf_ctrl(i, TUSB_DIR_IN); + + io_rw_32 *buf_reg32 = get_buf_ctrl(i, TUSB_DIR_IN); io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg32; // Check each buffer half: idle when both FULL and AVAIL are clear. @@ -274,24 +275,18 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { BUSY_MASK = USB_BUF_CTRL_FULL | USB_BUF_CTRL_AVAIL }; - uint16_t buf0, buf1; - const bool use_buf0 = !(buf_reg16[0] & BUSY_MASK); - - if (use_buf0) { - buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); - } - - const bool use_buf1 = (ep->remaining_len > 0) && !(buf_reg16[1] & BUSY_MASK); - if (use_buf1) { - buf1 = bufctrl_prepare16(ep, ep->dpram_buf + 64, false); - } + const bool buf0_idle = !(buf_reg16[0] & BUSY_MASK); + const bool buf1_idle = (ep->remaining_len > 0) && !(buf_reg16[1] & BUSY_MASK); - if (use_buf0 && use_buf1) { - buf0 |= USB_BUF_CTRL_SEL; // reset to buf0 since order of complete is not guaranteed - bufctrl_write32(buf_reg32, buf0 | ((uint32_t)buf1 << 16)); - } else if (use_buf0) { + if (buf0_idle && buf1_idle) { + // both are idle, start fresh + io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); + hw_endpoint_buffer_start(ep, ep_reg, buf_reg32); + } else if (buf0_idle) { + uint16_t buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); bufctrl_write16(buf_reg16, buf0); - } else if (use_buf1) { + } else if (buf1_idle) { + uint16_t buf1 = bufctrl_prepare16(ep, ep->dpram_buf + 64, false); bufctrl_write16(buf_reg16 + 1, buf1); } } -- cgit v1.3.1 From d9d28b7e94aac295e08db5fff8649e1467a37399 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Mar 2026 17:14:37 +0700 Subject: rename and clean up --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 33 ++-- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 235 ++++++++++++++------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 83 +++------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 27 +-- 4 files changed, 159 insertions(+), 219 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index ea791f4f6..b52c3114f 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -148,14 +148,9 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) { while ((usb_hw->abort_done & abort_mask) != abort_mask) {} } - uint32_t buf_ctrl = USB_BUF_CTRL_SEL; // reset to buffer 0 - if (ep->next_pid) { - buf_ctrl |= USB_BUF_CTRL_DATA1_PID; - } - - io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); - hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl); - hw_endpoint_reset_transfer(ep); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + *buf_reg = 0; // clear buffer control + rp2usb_reset_transfer(ep); if (rp2040_chip_version() >= 2) { usb_hw_clear->abort_done = abort_mask; @@ -185,9 +180,9 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { usb_hw_clear->buf_status = bit; buf_status &= ~bit; - if (hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id)) { + if (rp2usb_xfer_continue(ep, ep_reg, buf_reg, buf_id)) { const uint16_t xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); + rp2usb_reset_transfer(ep); dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); } } @@ -281,7 +276,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { if (buf0_idle && buf1_idle) { // both are idle, start fresh io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); - hw_endpoint_buffer_start(ep, ep_reg, buf_reg32); + rp2usb_buffer_start(ep, ep_reg, buf_reg32); } else if (buf0_idle) { uint16_t buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); bufctrl_write16(buf_reg16, buf0); @@ -534,7 +529,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); - hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, total_bytes); + rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, total_bytes); return true; } @@ -545,7 +540,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir); io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); - hw_endpoint_xfer_start(ep, ep_reg, buf_reg, NULL, ff, total_bytes); + rp2usb_xfer_start(ep, ep_reg, buf_reg, NULL, ff, total_bytes); return true; } #endif @@ -554,15 +549,17 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; const uint8_t epnum = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); + hw_endpoint_t *ep = hw_endpoint_get(epnum, dir); if (epnum == 0) { // A stall on EP0 has to be armed so it can be cleared on the next setup packet usb_hw_set->ep_stall_arm = (dir == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS; } - // stall and clear current pending buffer, may need to use EP_ABORT - io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); - hwbuf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL); + // abort first then stall and clear current pending buffer + hw_endpoint_abort_xfer(ep); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + *buf_reg = USB_BUF_CTRL_STALL; } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -573,8 +570,8 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { if (epnum != 0) { struct hw_endpoint* ep = hw_endpoint_get(epnum, dir); ep->next_pid = 0; // reset data toggle - io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir); - hwbuf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL); + io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir); + *buf_reg = 0; } } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 3f4c95422..379ba075d 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -119,14 +119,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { // forward declaration static void __tusb_irq_path_func(edpt_schedule_next)(void); TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value); -static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); +static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; uint8_t ep_addr = ep->ep_addr; uint xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); + rp2usb_reset_transfer(ep); hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); // Schedule next pending EPX transfer (only for non-interrupt endpoints) @@ -150,7 +150,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - if (hw_endpoint_xfer_continue(epx, ep_reg, buf_reg, buf_id)) { + if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id)) { hw_xfer_complete(epx, XFER_RESULT_SUCCESS); } } @@ -174,7 +174,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { if (ep->interrupt_num == epnum) { io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); - const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, 0); + const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0); if (done) { hw_xfer_complete(ep, XFER_RESULT_SUCCESS); } @@ -185,8 +185,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } // All non-interrupt endpoints use shared EPX. -// Forward declared above hw_xfer_complete, defined after edpt_xfer below. - +// Forward declared above hw_xfer_complete, defined after epx_xfer below. // Save current EPX context, mark pending, switch to next_ep static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; @@ -214,7 +213,7 @@ static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { } else { uint16_t prev_xferred = next_ep->xferred_len; next_ep->pending = 0; - edpt_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); + epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); epx->xferred_len += prev_xferred; } } @@ -344,57 +343,6 @@ void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { hcd_rp2040_irq(); } -static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { - const uint8_t ep_addr = ep_desc->bEndpointAddress; - const uint16_t wMaxPacketSize = tu_edpt_packet_size(ep_desc); - const uint8_t transfer_type = ep_desc->bmAttributes.xfer; - // const uint8_t bmInterval = ep_desc->bInterval; - - ep->max_packet_size = wMaxPacketSize; - ep->ep_addr = ep_addr; - ep->dev_addr = dev_addr; - ep->transfer_type = transfer_type; - ep->need_pre = need_pre(dev_addr); - ep->next_pid = 0u; - - if (transfer_type != TUSB_XFER_INTERRUPT) { - ep->dpram_buf = usbh_dpram->epx_data; - } else { - // from 15 interrupt endpoints pool - uint8_t int_idx; - for (int_idx = 0; int_idx < USB_HOST_INTERRUPT_ENDPOINTS; int_idx++) { - if (!tu_bit_test(usb_hw->int_ep_ctrl, 1 + int_idx)) { - ep->interrupt_num = int_idx + 1; - break; - } - } - assert(int_idx < USB_HOST_INTERRUPT_ENDPOINTS); - assert(ep_desc->bInterval > 0); - - //------------- dpram buf -------------// - // 15x64 last bytes of DPRAM for interrupt endpoint buffers - ep->dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); - uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf) | - (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); - usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; - - //------------- address control -------------// - const uint8_t epnum = tu_edpt_number(ep_addr); - uint32_t addr_ctrl = (uint32_t)(dev_addr | (epnum << USB_ADDR_ENDP1_ENDPOINT_LSB)); - if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { - addr_ctrl |= USB_ADDR_ENDP1_INTEP_DIR_BITS; - } - if (ep->need_pre) { - addr_ctrl |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; - } - usb_hw->int_ep_addr_ctrl[int_idx] = addr_ctrl; - - // Finally, activate interrupt endpoint - usb_hw_set->int_ep_ctrl = 1u << ep->interrupt_num; - } -} - //--------------------------------------------------------------------+ // HCD API //--------------------------------------------------------------------+ @@ -528,7 +476,55 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); hw_endpoint_t *ep = edpt_alloc(); TU_ASSERT(ep); - hw_endpoint_init(ep, dev_addr, ep_desc); + + const uint8_t ep_addr = ep_desc->bEndpointAddress; + const uint16_t max_packet_size = tu_edpt_packet_size(ep_desc); + const uint8_t transfer_type = ep_desc->bmAttributes.xfer; + // const uint8_t bmInterval = ep_desc->bInterval; + + ep->max_packet_size = max_packet_size; + ep->ep_addr = ep_addr; + ep->dev_addr = dev_addr; + ep->transfer_type = transfer_type; + ep->need_pre = need_pre(dev_addr); + ep->next_pid = 0u; + + if (transfer_type != TUSB_XFER_INTERRUPT) { + ep->dpram_buf = usbh_dpram->epx_data; + } else { + // from 15 interrupt endpoints pool + uint8_t int_idx; + for (int_idx = 0; int_idx < USB_HOST_INTERRUPT_ENDPOINTS; int_idx++) { + if (!tu_bit_test(usb_hw->int_ep_ctrl, 1 + int_idx)) { + ep->interrupt_num = int_idx + 1; + break; + } + } + assert(int_idx < USB_HOST_INTERRUPT_ENDPOINTS); + assert(ep_desc->bInterval > 0); + + //------------- dpram buf -------------// + // 15x64 last bytes of DPRAM for interrupt endpoint buffers + ep->dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); + uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf) | + (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; + + //------------- address control -------------// + const uint8_t epnum = tu_edpt_number(ep_addr); + uint32_t addr_ctrl = (uint32_t)(dev_addr | (epnum << USB_ADDR_ENDP1_ENDPOINT_LSB)); + if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) { + addr_ctrl |= USB_ADDR_ENDP1_INTEP_DIR_BITS; + } + if (ep->need_pre) { + addr_ctrl |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS; + } + usb_hw->int_ep_addr_ctrl[int_idx] = addr_ctrl; + + // Finally, activate interrupt endpoint + usb_hw_set->int_ep_ctrl = 1u << ep->interrupt_num; + } return true; } @@ -551,54 +547,51 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; } -static void edpt_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { - if (ep->transfer_type == TUSB_XFER_INTERRUPT) { - // For interrupt endpoint control and buffer is already configured - // Note: Interrupt is single buffered only - io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); - io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); - hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); - } else { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - - // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. - // The buffer selector toggles even in single-buffered mode, so the previous transfer's status - // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. -#if defined(PICO_RP2040) && PICO_RP2040 == 1 - usbh_dpram->epx_buf_ctrl = 0; -#endif +// start a transfer on epx endpoint +static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - // ep control - const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); - const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; - usbh_dpram->epx_ctrl = ep_ctrl; + // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. + // The buffer selector toggles even in single-buffered mode, so the previous transfer's status + // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. + #if defined(PICO_RP2040) && PICO_RP2040 == 1 + usbh_dpram->epx_buf_ctrl = 0; + #endif - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; - io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); + // ep control + const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); + const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + usbh_dpram->epx_ctrl = ep_ctrl; - // addr control - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); - epx = ep; + // addr control + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - // start transfer - const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); - } + epx = ep; + + // start transfer + const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | + (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); + sie_start_xfer(sie_ctrl); } // Schedule next pending EPX transfer from ISR context static void __tusb_irq_path_func(edpt_schedule_next)(void) { // EPX may already be active if the completion callback started a new transfer - if (epx->active) return; + if (epx->active) { + return; + } for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hw_endpoint_t *ep = &ep_pool[i]; - if (ep->pending == 0) continue; + if (ep->pending == 0) { + continue; + } if (ep->pending == 2) { // Pending setup: DPRAM already has the setup packet @@ -618,7 +611,7 @@ static void __tusb_irq_path_func(edpt_schedule_next)(void) { // Pending data transfer: preserve partial progress from preemption uint16_t prev_xferred = ep->xferred_len; ep->pending = 0; - edpt_xfer(ep, ep->user_buf, NULL, ep->remaining_len); + epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len); epx->xferred_len += prev_xferred; // restore partial progress } return; // start only one transfer @@ -631,31 +624,39 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b hw_endpoint_t *ep = edpt_find(dev_addr, ep_addr); TU_ASSERT(ep); - // Control endpoint can change direction 0x00 <-> 0x80 - if (ep_addr != ep->ep_addr) { - ep->ep_addr = ep_addr; - ep->next_pid = 1; // data and status stage start with DATA1 - } + if (ep->transfer_type == TUSB_XFER_INTERRUPT) { + // For interrupt endpoint control and buffer is already configured + // Note: Interrupt is single buffered only + io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); + io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); + rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); + } else { + // Control endpoint can change direction 0x00 <-> 0x80 when changing stages + if (ep_addr != ep->ep_addr) { + ep->ep_addr = ep_addr; + ep->next_pid = 1; // data and status stage start with DATA1 + } - // If EPX is busy with another transfer, mark as pending - if (ep->transfer_type != TUSB_XFER_INTERRUPT && epx->active) { - ep->user_buf = buffer; - ep->remaining_len = buflen; - ep->pending = 1; -#ifdef HAS_STOP_EPX_ON_NAK - usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; -#else - // Only enable SOF preemption for non-control endpoints - if (tu_edpt_number(epx->ep_addr) != 0) { - usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | - (300 << USB_NAK_POLL_DELAY_LS_LSB); - usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; + // If EPX is busy with another transfer, mark as pending + if (epx->active) { + ep->user_buf = buffer; + ep->remaining_len = buflen; + ep->pending = 1; + + #ifdef HAS_STOP_EPX_ON_NAK + usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; + #else + // Only enable SOF round-robin for non-control endpoints + if (tu_edpt_number(epx->ep_addr) != 0) { + usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | (300 << USB_NAK_POLL_DELAY_LS_LSB); + usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; + } + #endif + return true; } -#endif - return true; - } - edpt_xfer(ep, buffer, NULL, buflen); + epx_xfer(ep, buffer, NULL, buflen); + } return true; } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 433bd261d..44191092e 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -96,7 +96,7 @@ void rp2usb_init(void) { TU_LOG2_INT(sizeof(hw_endpoint_t)); } -void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { +void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) { ep->active = false; ep->remaining_len = 0; ep->xferred_len = 0; @@ -104,61 +104,23 @@ void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->is_xfer_fifo = false; } -void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask) { - const bool is_host = rp2usb_is_host_mode(); - uint32_t value = 0; - uint32_t buf_ctrl = *buf_ctrl_reg; - - if (and_mask) { - value = buf_ctrl & and_mask; - } - - if (or_mask) { - value |= or_mask; - if (or_mask & USB_BUF_CTRL_AVAIL) { - if (buf_ctrl & USB_BUF_CTRL_AVAIL) { - if (is_host) { -#if defined(PICO_RP2040) && PICO_RP2040 == 1 - // RP2040-E4: host buffer selector toggles in single-buffered mode, causing status - // to be written to BUF1 half and leaving stale AVAILABLE in BUF0 half. Clear it. - *buf_ctrl_reg = 0; -#else - panic("buf_ctrl @ 0x%lX already available (host)", (uintptr_t)buf_ctrl_reg); -#endif - } else { - panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_ctrl_reg); - } - } - *buf_ctrl_reg = value & ~USB_BUF_CTRL_AVAIL; - - // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, - // wait for USB controller to see the update before setting AVAILABLE. - // Don't need delay in host mode as host is in charge of when to start the transaction. - if (!is_host) { - busy_wait_at_least_cycles(12); - } - } - } - - *buf_ctrl_reg = value; -} - void __tusb_irq_path_func(bufctrl_write32)(io_rw_32 *buf_reg, uint32_t value) { const uint32_t current = *buf_reg; const uint32_t avail_mask = USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16); if (current & value & avail_mask) { panic("buf_ctrl @ 0x%lX already available", (uintptr_t)buf_reg); } - *buf_reg = value & ~USB_BUF_CTRL_AVAIL; // write other bits first + *buf_reg = value & ~(USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16)); // write other bits first // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, // wait for USB controller to see the update before setting AVAILABLE. // Don't need delay in host mode as host is in charge of when to start the transaction. - if (!rp2usb_is_host_mode() && (value & (USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16)))) { - busy_wait_at_least_cycles(12); + if (value & (USB_BUF_CTRL_AVAIL | (USB_BUF_CTRL_AVAIL << 16))) { + if (!rp2usb_is_host_mode()) { + busy_wait_at_least_cycles(12); + } + *buf_reg = value; // then set AVAILABLE bit last } - - *buf_reg = value; // then set AVAILABLE bit (if set) last } void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) { @@ -171,10 +133,12 @@ void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, // wait for USB controller to see the update before setting AVAILABLE. // Don't need delay in host mode as host is in charge of when to start the transaction. - if (!rp2usb_is_host_mode() && (value & USB_BUF_CTRL_AVAIL)) { - busy_wait_at_least_cycles(12); + if (value & USB_BUF_CTRL_AVAIL) { + if (!rp2usb_is_host_mode()) { + busy_wait_at_least_cycles(12); + } + *buf_reg16 = value; // then set AVAILABLE bit last } - *buf_reg16 = value; // then set AVAILABLE bit (if set) last } // prepare buffer, move data if tx, return buffer control @@ -217,7 +181,7 @@ uint16_t __tusb_irq_path_func(bufctrl_prepare16)(struct hw_endpoint *ep, uint8_t } // Start transaction on hw buffer -void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { +void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); const bool is_host = rp2usb_is_host_mode(); @@ -228,14 +192,6 @@ void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_r is_rx = (dir == TUSB_DIR_OUT); } - // RP2040-E4 (host only): in single-buffered multi-packet transfers, the controller may write completion status to - // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 - // since it leave BUF0 intact, which can be polled before buf_status interrupt is trigger. - // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. - #if CFG_TUSB_RP2_ERRATA_E4 - - #endif - // always compute and start with buffer 0 uint32_t buf_ctrl = bufctrl_prepare16(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; @@ -268,15 +224,15 @@ void __tusb_irq_path_func(hw_endpoint_buffer_start)(struct hw_endpoint *ep, io_r bufctrl_write32(buf_reg, buf_ctrl); } -void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, - uint16_t total_len) { +void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, + uint16_t total_len) { (void) ff; hw_endpoint_lock_update(ep, 1); if (ep->active) { // TODO: Is this acceptable for interrupt packets? TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr); - hw_endpoint_reset_transfer(ep); + rp2usb_reset_transfer(ep); } // Fill in info now that we're kicking off the hw @@ -310,7 +266,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * // all data has been received, no need to start hw transfer ep->active = false; const uint16_t xferred_len = ep->xferred_len; - hw_endpoint_reset_transfer(ep); + rp2usb_reset_transfer(ep); const bool is_host = rp2usb_is_host_mode(); #if CFG_TUH_ENABLED @@ -342,7 +298,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 * } #endif - hw_endpoint_buffer_start(ep, ep_reg, buf_reg); + rp2usb_buffer_start(ep, ep_reg, buf_reg); hw_endpoint_lock_update(ep, -1); } @@ -382,7 +338,8 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, // Returns true if transfer is complete. // buf_id: which buffer completed (from BUFF_CPU_SHOULD_HANDLE, only used for double-buffered). -bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id) { +bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, + uint8_t buf_id) { hw_endpoint_lock_update(ep, 1); const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 7b79cd2b1..34687020f 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -132,11 +132,11 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { //--------------------------------------------------------------------+ // Hardware Endpoint //--------------------------------------------------------------------+ -void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, - uint16_t total_len); -bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id); -void hw_endpoint_buffer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); -void hw_endpoint_reset_transfer(struct hw_endpoint *ep); +void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, + uint16_t total_len); +bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id); +void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); +void rp2usb_reset_transfer(hw_endpoint_t *ep); TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { // todo add critsec as necessary to prevent issues between worker and IRQ... @@ -147,26 +147,11 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct //--------------------------------------------------------------------+ // Hardware Buffer //--------------------------------------------------------------------+ -void hwbuf_ctrl_update(io_rw_32 *buf_ctrl_reg, uint32_t and_mask, uint32_t or_mask); - void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value); void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value); - uint16_t bufctrl_prepare16(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx); -TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwbuf_ctrl_update(buf_ctrl_reg, 0, value); -} - -TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_set_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwbuf_ctrl_update(buf_ctrl_reg, ~value, value); -} - -TU_ATTR_ALWAYS_INLINE static inline void hwbuf_ctrl_clear_mask(io_rw_32 *buf_ctrl_reg, uint32_t value) { - hwbuf_ctrl_update(buf_ctrl_reg, ~value, 0); -} - -static inline uintptr_t hw_data_offset(uint8_t *buf) { +TU_ATTR_ALWAYS_INLINE static inline uintptr_t hw_data_offset(uint8_t *buf) { // Remove usb base from buffer pointer return (uintptr_t)buf ^ (uintptr_t)usb_dpram; } -- cgit v1.3.1 From a9eb36b0fb5ad5322cb711954c98bf172faa1e06 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Mar 2026 19:01:57 +0700 Subject: refactor --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 266 +++++++++++++-------------- 1 file changed, 133 insertions(+), 133 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 379ba075d..8145cfdcc 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -116,12 +116,119 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } -// forward declaration -static void __tusb_irq_path_func(edpt_schedule_next)(void); -TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value); +//--------------------------------------------------------------------+ +// EPX +//--------------------------------------------------------------------+ + +static void __tusb_irq_path_func(epx_schedule_next)(void); static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); -static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) { +TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, tusb_dir_t ep_dir, bool need_pre) { + uint32_t value = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits + if (send_setup) { + value |= USB_SIE_CTRL_SEND_SETUP_BITS; + } else { + value |= (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); + } + if (need_pre) { + value |= USB_SIE_CTRL_PREAMBLE_EN_BITS; + } + + // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit + // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". + // We write everything except the START_TRANS bit first, then wait some cycles. + usb_hw->sie_ctrl = value; + busy_wait_at_least_cycles(12); + usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; +} + +// All non-interrupt endpoints use shared EPX. +// Save current EPX context, mark pending, switch to next_ep +static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { + const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; + const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; + epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); + epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0; + if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { + epx->user_buf -= buf0_len; + } + epx->pending = 1; + epx->active = false; + usbh_dpram->epx_buf_ctrl = 0; + + if (next_ep->pending == 2) { + next_ep->ep_addr = 0; + next_ep->remaining_len = 8; + next_ep->xferred_len = 0; + next_ep->active = true; + next_ep->pending = 0; + epx = next_ep; + usb_hw->dev_addr_ctrl = next_ep->dev_addr; + sie_start_xfer(true, TUSB_DIR_OUT, next_ep->need_pre); + } else { + uint16_t prev_xferred = next_ep->xferred_len; + next_ep->pending = 0; + epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); + epx->xferred_len += prev_xferred; + } +} + +// Round-robin find next pending ep after current epx +static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ep) { + const uint cur_idx = (uint)(cur_ep - &ep_pool[0]); + for (uint i = cur_idx + 1; i < TU_ARRAY_SIZE(ep_pool); i++) { + if (ep_pool[i].pending) { + return &ep_pool[i]; + } + } + for (uint i = 0; i < cur_idx; i++) { + if (ep_pool[i].pending) { + return &ep_pool[i]; + } + } + return NULL; +} + +// Schedule next pending EPX transfer from ISR context +static void __tusb_irq_path_func(epx_schedule_next)(void) { + // EPX may already be active if the completion callback started a new transfer + // if (epx->active) { + // return; + // } + + for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + hw_endpoint_t *ep = &ep_pool[i]; + if (ep->pending == 0) { + continue; + } + + if (ep->pending == 2) { + // Pending setup: DPRAM already has the setup packet + ep->ep_addr = 0; + ep->remaining_len = 8; + ep->xferred_len = 0; + ep->active = true; + ep->pending = 0; + + epx = ep; + usb_hw->dev_addr_ctrl = ep->dev_addr; + + sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); + } else { + // Pending data transfer: preserve partial progress from preemption + uint16_t prev_xferred = ep->xferred_len; + ep->pending = 0; + epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len); + epx->xferred_len += prev_xferred; // restore partial progress + } + return; // start only one transfer + } +} + +//--------------------------------------------------------------------+ +// Interrupt handlers +//--------------------------------------------------------------------+ +static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_result_t xfer_result) { // Mark transfer as done before we tell the tinyusb stack uint8_t dev_addr = ep->dev_addr; uint8_t ep_addr = ep->ep_addr; @@ -131,17 +238,21 @@ static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_resul // Schedule next pending EPX transfer (only for non-interrupt endpoints) if (ep == epx) { - edpt_schedule_next(); + epx_schedule_next(); + // hw_endpoint_t *next_ep = epx_next_pending(epx); + // if (next_ep != NULL) { + // epx_switch_ep(next_ep); + // } } } -static void __tusb_irq_path_func(handle_hwbuf_status)(void) { +static void __tusb_irq_path_func(handle_buf_status_isr)(void) { pico_trace("buf_status 0x%08lx\n", buf_status); enum { BUF_STATUS_EPX = 1u }; - // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0. + // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0.3 // Double-buffered: if both buffers completed at once, buf_status re-sets // immediately after clearing (datasheet Table 406). Process the second buffer too. while (usb_hw->buf_status & BUF_STATUS_EPX) { @@ -151,7 +262,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id)) { - hw_xfer_complete(epx, XFER_RESULT_SUCCESS); + xfer_complete_isr(epx, XFER_RESULT_SUCCESS); } } @@ -176,7 +287,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0); if (done) { - hw_xfer_complete(ep, XFER_RESULT_SUCCESS); + xfer_complete_isr(ep, XFER_RESULT_SUCCESS); } break; } @@ -184,56 +295,6 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) { } } -// All non-interrupt endpoints use shared EPX. -// Forward declared above hw_xfer_complete, defined after epx_xfer below. -// Save current EPX context, mark pending, switch to next_ep -static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { - const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; - const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; - epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); - epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0; - if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { - epx->user_buf -= buf0_len; - } - epx->pending = 1; - epx->active = false; - usbh_dpram->epx_buf_ctrl = 0; - - if (next_ep->pending == 2) { - next_ep->ep_addr = 0; - next_ep->remaining_len = 8; - next_ep->xferred_len = 0; - next_ep->active = true; - next_ep->pending = 0; - epx = next_ep; - usb_hw->dev_addr_ctrl = next_ep->dev_addr; - const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS | - (next_ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sc); - } else { - uint16_t prev_xferred = next_ep->xferred_len; - next_ep->pending = 0; - epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); - epx->xferred_len += prev_xferred; - } -} - -// Round-robin find next pending ep after current epx -static hw_endpoint_t *__tusb_irq_path_func(epx_find_pending)(void) { - const uint start = (uint)(epx - &ep_pool[0]) + 1; - for (uint i = start; i < TU_ARRAY_SIZE(ep_pool); i++) { - if (ep_pool[i].pending) { - return &ep_pool[i]; - } - } - for (uint i = 0; i < start - 1; i++) { - if (ep_pool[i].pending) { - return &ep_pool[i]; - } - } - return NULL; -} - static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { const uint32_t status = usb_hw->ints; @@ -258,11 +319,11 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // AND TRANS_COMPLETE as the stall is an alternative response // to one of those events usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; - hw_xfer_complete(epx, XFER_RESULT_STALLED); + xfer_complete_isr(epx, XFER_RESULT_STALLED); } if (status & USB_INTS_BUFF_STATUS_BITS) { - handle_hwbuf_status(); + handle_buf_status_isr(); } if (status & USB_INTS_TRANS_COMPLETE_BITS) { @@ -271,32 +332,23 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // only handle a setup packet if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { epx->xferred_len = 8; - hw_xfer_complete(epx, XFER_RESULT_SUCCESS); + xfer_complete_isr(epx, XFER_RESULT_SUCCESS); } else { // Don't care. Will handle this in buff status } } #ifdef HAS_STOP_EPX_ON_NAK - // RP2350: hardware stops EPX on NAK automatically if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; - hw_endpoint_t *next_ep = NULL; - if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { - next_ep = epx_find_pending(); - } - if (next_ep) { + hw_endpoint_t *next_ep = epx_next_pending(epx); + if (next_ep != NULL) { epx_switch_ep(next_ep); } else { - // No preemption: disable stop-on-NAK, restart current transfer + // No switch: disable stop-on-NAK, restart current transfer usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; - if (epx->active) { - const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr); - const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); - } + sie_start_xfer(false, tu_edpt_dir(epx->ep_addr), epx->need_pre); } } #else @@ -304,22 +356,22 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { if (status & USB_INTS_HOST_SOF_BITS) { (void) usb_hw->sof_rd; // clear SOF by reading SOF_RD if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { - hw_endpoint_t *next_ep = epx_find_pending(); + hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep) { usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} busy_wait_at_least_cycles(12); if (usb_hw->buf_status & 1u) { usb_hw->nak_poll = USB_NAK_POLL_RESET; - handle_hwbuf_status(); + handle_buf_status_isr(); } else { epx_switch_ep(next_ep); } } else { usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; - usb_hw->nak_poll = USB_NAK_POLL_RESET; + usb_hw->nak_poll = USB_NAK_POLL_RESET; } - } else if (!epx_find_pending()) { + } else if (!epx_next_pending(epx)) { // EPX is on control endpoint or inactive — disable SOF if nothing pending usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; usb_hw->nak_poll = USB_NAK_POLL_RESET; @@ -536,17 +588,6 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } -TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) { - value |= (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK); // preserve base bits - - // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit - // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". - // We write everything except the START_TRANS bit first, then wait some cycles. - usb_hw->sie_ctrl = value; - busy_wait_at_least_cycles(12); - usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; -} - // start a transfer on epx endpoint static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { const uint8_t ep_num = tu_edpt_number(ep->ep_addr); @@ -575,47 +616,7 @@ static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t epx = ep; // start transfer - const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) | - (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); -} - -// Schedule next pending EPX transfer from ISR context -static void __tusb_irq_path_func(edpt_schedule_next)(void) { - // EPX may already be active if the completion callback started a new transfer - if (epx->active) { - return; - } - - for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hw_endpoint_t *ep = &ep_pool[i]; - if (ep->pending == 0) { - continue; - } - - if (ep->pending == 2) { - // Pending setup: DPRAM already has the setup packet - ep->ep_addr = 0; - ep->remaining_len = 8; - ep->xferred_len = 0; - ep->active = true; - ep->pending = 0; - - epx = ep; - usb_hw->dev_addr_ctrl = ep->dev_addr; - - const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | - (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); - } else { - // Pending data transfer: preserve partial progress from preemption - uint16_t prev_xferred = ep->xferred_len; - ep->pending = 0; - epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len); - epx->xferred_len += prev_xferred; // restore partial progress - } - return; // start only one transfer - } + sie_start_xfer(false, ep_dir, ep->need_pre); } bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { @@ -705,8 +706,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet usb_hw->dev_addr_ctrl = dev_addr; // Set device address // Set pre if we are a low speed device on full speed hub - const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0); - sie_start_xfer(sie_ctrl); + sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); return true; } -- cgit v1.3.1 From 7d004257d9cb849e90166a50c0f751b162fd6a68 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 28 Mar 2026 12:12:20 +0700 Subject: host epx clean up --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 211 +++++++++++---------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 9 +- src/portable/raspberrypi/rp2040/rp2040_usb.h | 14 +- 3 files changed, 107 insertions(+), 127 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 8145cfdcc..167a80a7a 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -44,11 +44,11 @@ #include "host/hcd.h" #include "host/usbh.h" -// port 0 is native USB port, other is counted as software PIO -#define RHPORT_NATIVE 0 + // port 0 is native USB port, other is counted as software PIO + #define RHPORT_NATIVE 0 //--------------------------------------------------------------------+ -// Low level rp2040 controller functions +// //--------------------------------------------------------------------+ // Host mode uses one shared endpoint register for non-interrupt endpoint @@ -119,10 +119,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { //--------------------------------------------------------------------+ // EPX //--------------------------------------------------------------------+ - -static void __tusb_irq_path_func(epx_schedule_next)(void); -static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); - TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, tusb_dir_t ep_dir, bool need_pre) { uint32_t value = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits if (send_setup) { @@ -142,34 +138,59 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, tusb_di usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; } -// All non-interrupt endpoints use shared EPX. -// Save current EPX context, mark pending, switch to next_ep -static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) { +// prepare epx_ctrl register for new endpoint +TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(hw_endpoint_t *ep) { + // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. + // The buffer selector toggles even in single-buffered mode, so the previous transfer's status + // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. + #if defined(PICO_RP2040) && PICO_RP2040 == 1 + usbh_dpram->epx_buf_ctrl = 0; + #endif + + // ep control + const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | + ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf); + usbh_dpram->epx_ctrl = ep_ctrl; +} + +// save on-going context +static void __tusb_irq_path_func(epx_save_context)(void) { const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; - const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; + const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; // TODO handle double buffered case epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0; if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { epx->user_buf -= buf0_len; } - epx->pending = 1; - epx->active = false; + epx->pending = 1; + epx->active = false; + usbh_dpram->epx_buf_ctrl = 0; +} + +// All non-interrupt endpoints use shared EPX. +// Save current EPX context, mark pending, switch to ep +static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { + const bool is_setup = (ep->pending == 2); + + epx = ep; // switch pointer + ep->pending = 0; + ep->active = true; - if (next_ep->pending == 2) { - next_ep->ep_addr = 0; - next_ep->remaining_len = 8; - next_ep->xferred_len = 0; - next_ep->active = true; - next_ep->pending = 0; - epx = next_ep; - usb_hw->dev_addr_ctrl = next_ep->dev_addr; - sie_start_xfer(true, TUSB_DIR_OUT, next_ep->need_pre); + if (is_setup) { + usb_hw->dev_addr_ctrl = ep->dev_addr; + sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); } else { - uint16_t prev_xferred = next_ep->xferred_len; - next_ep->pending = 0; - epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len); - epx->xferred_len += prev_xferred; + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + + epx_ctrl_prepare(ep); + rp2usb_buffer_start(ep, ep_reg, buf_reg); + + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); + sie_start_xfer(false, ep_dir, ep->need_pre); // start transfer } } @@ -189,41 +210,6 @@ static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ return NULL; } -// Schedule next pending EPX transfer from ISR context -static void __tusb_irq_path_func(epx_schedule_next)(void) { - // EPX may already be active if the completion callback started a new transfer - // if (epx->active) { - // return; - // } - - for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { - hw_endpoint_t *ep = &ep_pool[i]; - if (ep->pending == 0) { - continue; - } - - if (ep->pending == 2) { - // Pending setup: DPRAM already has the setup packet - ep->ep_addr = 0; - ep->remaining_len = 8; - ep->xferred_len = 0; - ep->active = true; - ep->pending = 0; - - epx = ep; - usb_hw->dev_addr_ctrl = ep->dev_addr; - - sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); - } else { - // Pending data transfer: preserve partial progress from preemption - uint16_t prev_xferred = ep->xferred_len; - ep->pending = 0; - epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len); - epx->xferred_len += prev_xferred; // restore partial progress - } - return; // start only one transfer - } -} //--------------------------------------------------------------------+ // Interrupt handlers @@ -236,13 +222,12 @@ static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_resu rp2usb_reset_transfer(ep); hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); - // Schedule next pending EPX transfer (only for non-interrupt endpoints) + // Carry more transfer on epx if (ep == epx) { - epx_schedule_next(); - // hw_endpoint_t *next_ep = epx_next_pending(epx); - // if (next_ep != NULL) { - // epx_switch_ep(next_ep); - // } + hw_endpoint_t *next_ep = epx_next_pending(epx); + if (next_ep != NULL) { + epx_switch_ep(next_ep); + } } } @@ -338,12 +323,13 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } } -#ifdef HAS_STOP_EPX_ON_NAK + #ifdef HAS_STOP_EPX_ON_NAK if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep != NULL) { + epx_save_context(); epx_switch_ep(next_ep); } else { // No switch: disable stop-on-NAK, restart current transfer @@ -412,7 +398,6 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Remove shared irq if it was previously added so as not to fill up shared irq slots irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq); - irq_add_shared_handler(USBCTRL_IRQ, hcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY); // clear epx and interrupt eps @@ -588,37 +573,6 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } -// start a transfer on epx endpoint -static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - - // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. - // The buffer selector toggles even in single-buffered mode, so the previous transfer's status - // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. - #if defined(PICO_RP2040) && PICO_RP2040 == 1 - usbh_dpram->epx_buf_ctrl = 0; - #endif - - // ep control - const uint32_t dpram_offset = hw_data_offset(ep->dpram_buf); - const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; - usbh_dpram->epx_ctrl = ep_ctrl; - - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; - io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, ff, total_len); - - // addr control - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - - epx = ep; - - // start transfer - sie_start_xfer(false, ep_dir, ep->need_pre); -} - bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void)rhport; @@ -639,6 +593,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b } // If EPX is busy with another transfer, mark as pending + rp2usb_critical_enter(); if (epx->active) { ep->user_buf = buffer; ep->remaining_len = buflen; @@ -653,10 +608,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; } #endif - return true; - } + } else { + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + + epx = ep; - epx_xfer(ep, buffer, NULL, buflen); + epx_ctrl_prepare(ep); + rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); // prepare bufctrl + + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); + sie_start_xfer(false, ep_dir, ep->need_pre); // start transfer + } + rp2usb_critical_exit(); } return true; @@ -681,33 +647,30 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); - ep->ep_addr = 0; // setup is OUT - - // If EPX is busy, mark as pending setup (DPRAM already has the packet) - if (epx->active) { - ep->pending = 2; -#ifdef HAS_STOP_EPX_ON_NAK - usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; -#else - if (tu_edpt_number(epx->ep_addr) != 0) { - usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | - (300 << USB_NAK_POLL_DELAY_LS_LSB); - usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; - } -#endif - return true; - } + rp2usb_critical_enter(); + ep->ep_addr = 0; // setup is OUT ep->remaining_len = 8; ep->xferred_len = 0; - ep->active = true; - epx = ep; - usb_hw->dev_addr_ctrl = dev_addr; // Set device address + // If EPX is busy, mark as pending setup (DPRAM already has the packet) + if (epx->active) { + ep->pending = 2; // setup + #ifdef HAS_STOP_EPX_ON_NAK + usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; + #else + usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | (300 << USB_NAK_POLL_DELAY_LS_LSB); + usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; + #endif + } else { + epx = ep; + ep->active = true; - // Set pre if we are a low speed device on full speed hub - sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); + usb_hw->dev_addr_ctrl = dev_addr; // Set device address + sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); // start transfer + } + rp2usb_critical_exit(); return true; } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 44191092e..15d4d723f 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -46,6 +46,8 @@ static bool e15_is_critical_frame_period(void); static uint8_t rp2040_chipversion = 2; #endif +critical_section_t rp2usb_lock; + //--------------------------------------------------------------------+ // Implementation //--------------------------------------------------------------------+ @@ -94,6 +96,8 @@ void rp2usb_init(void) { #endif TU_LOG2_INT(sizeof(hw_endpoint_t)); + + critical_section_init(&rp2usb_lock); } void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) { @@ -383,10 +387,11 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ const bool is_short = xact_bytes < ep->max_packet_size; const bool is_done = is_short || (buf_ctrl16 & USB_BUF_CTRL_LAST); - // short packet on rx with double buffer: abort the other half (if not last) and reset double-buffer state. + // Short packet on rx with double buffer: abort the other half (if not last) and reset double-buffer state. // The other buffer may be: (a) still AVAIL, (b) in-progress (controller receiving), or (c) already completed. // We must abort to safely reclaim it. If it has valid data (FULL), save as future for the next transfer. - // After abort, zero buf_ctrl + // After abort, zero buf_ctrl. + // Note: Host mode we cannot save next transfer data due to shared epx -> force single if (is_short && is_double && is_rx && !is_last) { io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); const uint32_t abort_bit = TU_BIT((tu_edpt_number(ep->ep_addr) << 1) | (dir ? 0 : 1)); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 34687020f..3b45c7c94 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -7,6 +7,8 @@ #include "hardware/resets.h" #include "hardware/timer.h" +#include "pico/critical_section.h" + #include "common/tusb_common.h" #include "osal/osal.h" #include "common/tusb_fifo.h" @@ -129,6 +131,15 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) { return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; } +extern critical_section_t rp2usb_lock; + +TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_enter(void) { + critical_section_enter_blocking(&rp2usb_lock); +} +TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_exit(void) { + critical_section_exit(&rp2usb_lock); +} + //--------------------------------------------------------------------+ // Hardware Endpoint //--------------------------------------------------------------------+ @@ -138,7 +149,8 @@ bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); void rp2usb_reset_transfer(hw_endpoint_t *ep); -TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) { + +TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint *ep, __unused int delta) { // todo add critsec as necessary to prevent issues between worker and IRQ... // note that this is perhaps as simple as disabling IRQs because it would make // sense to have worker and IRQ on same core, however I think using critsec is about equivalent. -- cgit v1.3.1 From dfcd271400c31e15a7d38b319c65173c3e4227c7 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 28 Mar 2026 23:27:39 +0700 Subject: rp2 common refactor --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 4 +- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 54 ++++---- src/portable/raspberrypi/rp2040/rp2040_usb.c | 179 +++++++++++++-------------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 6 +- 4 files changed, 117 insertions(+), 126 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index b52c3114f..d4c1bf708 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -180,7 +180,7 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { usb_hw_clear->buf_status = bit; buf_status &= ~bit; - if (rp2usb_xfer_continue(ep, ep_reg, buf_reg, buf_id)) { + if (rp2usb_xfer_continue(ep, ep_reg, buf_reg, buf_id, dir == TUSB_DIR_OUT)) { const uint16_t xferred_len = ep->xferred_len; rp2usb_reset_transfer(ep); dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); @@ -276,7 +276,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { if (buf0_idle && buf1_idle) { // both are idle, start fresh io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); - rp2usb_buffer_start(ep, ep_reg, buf_reg32); + rp2usb_buffer_start(ep, ep_reg, buf_reg32, false, false); } else if (buf0_idle) { uint16_t buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); bufctrl_write16(buf_reg16, buf0); diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 167a80a7a..31bbfc0be 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -35,8 +35,11 @@ #define HAS_STOP_EPX_ON_NAK #endif +// port 0 is native USB port, other is counted as software PIO + #define RHPORT_NATIVE 0 + //--------------------------------------------------------------------+ -// INCLUDE + // INCLUDE //--------------------------------------------------------------------+ #include "rp2040_usb.h" #include "osal/osal.h" @@ -44,9 +47,6 @@ #include "host/hcd.h" #include "host/usbh.h" - // port 0 is native USB port, other is counted as software PIO - #define RHPORT_NATIVE 0 - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -119,12 +119,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { //--------------------------------------------------------------------+ // EPX //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, tusb_dir_t ep_dir, bool need_pre) { +TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, bool is_rx, bool need_pre) { uint32_t value = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits if (send_setup) { value |= USB_SIE_CTRL_SEND_SETUP_BITS; } else { - value |= (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); + value |= (is_rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); } if (need_pre) { value |= USB_SIE_CTRL_PREAMBLE_EN_BITS; @@ -179,18 +179,18 @@ static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { if (is_setup) { usb_hw->dev_addr_ctrl = ep->dev_addr; - sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); + sie_start_xfer(true, false, ep->need_pre); } else { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; - io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + const uint8_t ep_num = tu_edpt_number(ep->ep_addr); + const bool is_rx = tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN; + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; epx_ctrl_prepare(ep); - rp2usb_buffer_start(ep, ep_reg, buf_reg); + rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, is_rx || ep->transfer_type == TUSB_XFER_INTERRUPT); usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - sie_start_xfer(false, ep_dir, ep->need_pre); // start transfer + sie_start_xfer(false, is_rx, ep->need_pre); // start transfer } } @@ -246,13 +246,13 @@ static void __tusb_irq_path_func(handle_buf_status_isr)(void) { io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id)) { + if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id, tu_edpt_dir(epx->ep_addr) == TUSB_DIR_IN)) { xfer_complete_isr(epx, XFER_RESULT_SUCCESS); } } // Check "interrupt" (asynchronous) endpoints for both IN and OUT - uint32_t buf_status = usb_hw->buf_status & ~1u; + uint32_t buf_status = usb_hw->buf_status & (uint32_t)~BUF_STATUS_EPX; while (buf_status) { // ctz/clz is faster than loop which has only a few bit set in general const uint8_t idx = (uint8_t) __builtin_ctz(buf_status); @@ -268,9 +268,9 @@ static void __tusb_irq_path_func(handle_buf_status_isr)(void) { for (size_t e = 0; e < TU_ARRAY_SIZE(ep_pool); e++) { hw_endpoint_t *ep = &ep_pool[e]; if (ep->interrupt_num == epnum) { - io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); - io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); - const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0); + io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); + io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); + const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN); if (done) { xfer_complete_isr(ep, XFER_RESULT_SUCCESS); } @@ -326,21 +326,20 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { #ifdef HAS_STOP_EPX_ON_NAK if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; - hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep != NULL) { epx_save_context(); epx_switch_ep(next_ep); } else { - // No switch: disable stop-on-NAK, restart current transfer + // No pending endpoint, this is the only active one: disable stop-on-NAK, continue current transfer usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; - sie_start_xfer(false, tu_edpt_dir(epx->ep_addr), epx->need_pre); + sie_start_xfer(false, TUSB_DIR_IN == tu_edpt_dir(epx->ep_addr), epx->need_pre); } } -#else + #else // RP2040: on SOF, stop and switch if there's a pending ep if (status & USB_INTS_HOST_SOF_BITS) { - (void) usb_hw->sof_rd; // clear SOF by reading SOF_RD + (void)usb_hw->sof_rd; // clear SOF by reading SOF_RD if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep) { @@ -360,10 +359,10 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } else if (!epx_next_pending(epx)) { // EPX is on control endpoint or inactive — disable SOF if nothing pending usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; - usb_hw->nak_poll = USB_NAK_POLL_RESET; + usb_hw->nak_poll = USB_NAK_POLL_RESET; } } -#endif + #endif if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; @@ -611,6 +610,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b } else { const uint8_t ep_num = tu_edpt_number(ep->ep_addr); const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); + const bool is_rx = (ep_dir == TUSB_DIR_IN); io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; @@ -620,7 +620,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); // prepare bufctrl usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - sie_start_xfer(false, ep_dir, ep->need_pre); // start transfer + sie_start_xfer(false, is_rx, ep->need_pre); // start transfer } rp2usb_critical_exit(); } @@ -667,7 +667,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet ep->active = true; usb_hw->dev_addr_ctrl = dev_addr; // Set device address - sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre); // start transfer + sie_start_xfer(true, false, ep->need_pre); // start transfer } rp2usb_critical_exit(); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 15d4d723f..49c948af6 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -58,7 +58,7 @@ static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) { } } -#if CFG_TUD_EDPT_DEDICATED_HWFIFO + #if CFG_TUD_EDPT_DEDICATED_HWFIFO void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) { (void)access_mode; unaligned_memcpy((uint8_t *)(uintptr_t)hwfifo, src, len); @@ -68,25 +68,25 @@ void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, co (void)access_mode; unaligned_memcpy(dest, (const uint8_t *)(uintptr_t)hwfifo, len); } -#endif + #endif void rp2usb_init(void) { // Reset usb controller reset_block(RESETS_RESET_USBCTRL_BITS); unreset_block_wait(RESETS_RESET_USBCTRL_BITS); -#ifdef __GNUC__ - // Clear any previous state just in case -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#if __GNUC__ > 6 -#pragma GCC diagnostic ignored "-Wstringop-overflow" -#endif -#endif + #ifdef __GNUC__ + // Clear any previous state just in case + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Warray-bounds" + #if __GNUC__ > 6 + #pragma GCC diagnostic ignored "-Wstringop-overflow" + #endif + #endif memset(usb_dpram, 0, sizeof(*usb_dpram)); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif // Mux the controller to the onboard usb phy usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; @@ -101,10 +101,10 @@ void rp2usb_init(void) { } void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) { - ep->active = false; + ep->active = false; ep->remaining_len = 0; - ep->xferred_len = 0; - ep->user_buf = 0; + ep->xferred_len = 0; + ep->user_buf = 0; ep->is_xfer_fifo = false; } @@ -134,9 +134,7 @@ void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) } *buf_reg16 = value & (uint16_t)~USB_BUF_CTRL_AVAIL; // write other bits first - // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access: after write to buffer control, - // wait for USB controller to see the update before setting AVAILABLE. - // Don't need delay in host mode as host is in charge of when to start the transaction. + // Section 4.1.2.7.1 (rp2040) / 12.7.3.7.1 (rp2350) Concurrent access if (value & USB_BUF_CTRL_AVAIL) { if (!rp2usb_is_host_mode()) { busy_wait_at_least_cycles(12); @@ -146,7 +144,7 @@ void __tusb_irq_path_func(bufctrl_write16)(io_rw_16 *buf_reg16, uint16_t value) } // prepare buffer, move data if tx, return buffer control -uint16_t __tusb_irq_path_func(bufctrl_prepare16)(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx) { +uint16_t __tusb_irq_path_func(bufctrl_prepare16)(hw_endpoint_t *ep, uint8_t *dpram_buf, bool is_rx) { const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size); ep->remaining_len -= buflen; @@ -158,13 +156,13 @@ uint16_t __tusb_irq_path_func(bufctrl_prepare16)(struct hw_endpoint *ep, uint8_t if (!is_rx) { if (buflen) { - // Copy data from user buffer/fifo to hw buffer - #if CFG_TUD_EDPT_DEDICATED_HWFIFO + // Copy data from user buffer/fifo to hw buffer + #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ep->is_xfer_fifo) { // not in sram, may mess up timing with E15 workaround tu_hwfifo_write_from_fifo(dpram_buf, ep->user_fifo, buflen, NULL); } else - #endif + #endif { unaligned_memcpy(dpram_buf, ep->user_buf, buflen); ep->user_buf += buflen; @@ -185,33 +183,13 @@ uint16_t __tusb_irq_path_func(bufctrl_prepare16)(struct hw_endpoint *ep, uint8_t } // Start transaction on hw buffer -void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) { - const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - const bool is_host = rp2usb_is_host_mode(); - - bool is_rx; - if (is_host) { - is_rx = (dir == TUSB_DIR_IN); - } else { - is_rx = (dir == TUSB_DIR_OUT); - } - +void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx, + bool force_single) { // always compute and start with buffer 0 uint32_t buf_ctrl = bufctrl_prepare16(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; // Note: device EP0 does not have an endpoint control register if (ep_reg != NULL) { - #if 1 - const bool force_single = (is_host && tu_edpt_number(ep->ep_addr) != 0); - #else - bool force_single = false; // is_rx; - #if CFG_TUH_ENABLED - if (is_host && ep->interrupt_num != 0) { - force_single = true; - } - #endif -#endif - uint32_t ep_ctrl = *ep_reg; if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data @@ -224,13 +202,13 @@ void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_r *ep_reg = ep_ctrl; } - // Finally, write to buffer_control which will trigger the transfer the next time the controller polls this endpoint + // Finally, write to buffer control which will trigger the transfer the next time the controller polls this endpoint bufctrl_write32(buf_reg, buf_ctrl); } void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) { - (void) ff; + (void)ff; hw_endpoint_lock_update(ep, 1); if (ep->active) { @@ -241,20 +219,22 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u // Fill in info now that we're kicking off the hw ep->remaining_len = total_len; - ep->xferred_len = 0; - ep->active = true; + ep->xferred_len = 0; + ep->active = true; -#if CFG_TUD_EDPT_DEDICATED_HWFIFO + #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ff != NULL) { ep->user_fifo = ff; ep->is_xfer_fifo = true; } else -#endif + #endif { ep->user_buf = buffer; ep->is_xfer_fifo = false; } + const bool is_host = rp2usb_is_host_mode(); + if (ep->future_len > 0) { // only on rx endpoint const uint8_t future_len = ep->future_len; @@ -272,7 +252,6 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u const uint16_t xferred_len = ep->xferred_len; rp2usb_reset_transfer(ep); - const bool is_host = rp2usb_is_host_mode(); #if CFG_TUH_ENABLED if (is_host) { hcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); @@ -302,12 +281,20 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u } #endif - rp2usb_buffer_start(ep, ep_reg, buf_reg); + const bool is_rx = (is_host == (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN)); + #if CFG_TUH_ENABLED + const bool force_single = (is_host && (is_rx || ep->transfer_type == TUSB_XFER_INTERRUPT)); + #else + const bool force_single = false; + #endif + + rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, force_single); hw_endpoint_lock_update(ep, -1); } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, uint16_t buf_ctrl, uint8_t *dpram_buf) { +static uint16_t __tusb_irq_path_func(bufctrl_sync16)(hw_endpoint_t *ep, bool is_rx, uint16_t buf_ctrl, + uint8_t *dpram_buf) { const uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; if (!is_rx) { @@ -342,17 +329,10 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, bool is_rx, // Returns true if transfer is complete. // buf_id: which buffer completed (from BUFF_CPU_SHOULD_HANDLE, only used for double-buffered). -bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, - uint8_t buf_id) { +bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id, + bool is_rx) { hw_endpoint_lock_update(ep, 1); - const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr); - const bool is_host = rp2usb_is_host_mode(); - const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT); - - io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg; - uint16_t buf_ctrl16 = *(buf_reg16 + buf_id); - if (!ep->active) { // probably land here due to short packet on rx with double buffered hw_endpoint_lock_update(ep, -1); @@ -360,7 +340,7 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ } const bool is_double = (ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS)); - (void)is_double; + const bool is_host = rp2usb_is_host_mode(); #if CFG_TUSB_RP2_ERRATA_E4 const bool need_e4_fix = (is_host && !is_double); @@ -372,7 +352,7 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 // since it leave BUF0 intact, which can be poll before buf_status interrupt is trigger. // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. - uint8_t* dpram_buf = ep->dpram_buf; + uint8_t *dpram_buf = ep->dpram_buf; if (buf_id) { #if CFG_TUSB_RP2_ERRATA_E4 if (!need_e4_fix) // incorrect buf_id, buffer pointer is still buf0 @@ -382,7 +362,10 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ } } - const uint16_t xact_bytes = hwbuf_sync(ep, is_rx, buf_ctrl16, dpram_buf); + io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg; + uint16_t buf_ctrl16 = *(buf_reg16 + buf_id); + + const uint16_t xact_bytes = bufctrl_sync16(ep, is_rx, buf_ctrl16, dpram_buf); const bool is_last = buf_ctrl16 & USB_BUF_CTRL_LAST; const bool is_short = xact_bytes < ep->max_packet_size; const bool is_done = is_short || (buf_ctrl16 & USB_BUF_CTRL_LAST); @@ -391,40 +374,48 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ // The other buffer may be: (a) still AVAIL, (b) in-progress (controller receiving), or (c) already completed. // We must abort to safely reclaim it. If it has valid data (FULL), save as future for the next transfer. // After abort, zero buf_ctrl. - // Note: Host mode we cannot save next transfer data due to shared epx -> force single + // Note: Host mode we cannot save next transfer data due to shared epx --> force single if (is_short && is_double && is_rx && !is_last) { - io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); - const uint32_t abort_bit = TU_BIT((tu_edpt_number(ep->ep_addr) << 1) | (dir ? 0 : 1)); - - #if CFG_TUSB_RP2_ERRATA_E2 - if (rp2040_chipversion >= 2) + #if CFG_TUH_ENABLED + if (is_host) {} #endif - { - usb_hw_set->abort = abort_bit; - while ((usb_hw->abort_done & abort_bit) != abort_bit) {} - } - // After abort, check if the other buffer received valid data - const uint16_t buf_ctrl16_other = *buf_reg16_other; - if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { - // Host already sent data into this buffer (e.g. write payload right after short CBW). - // Save it for the next transfer. - ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); - ep->future_bufid = buf_id ^ 1; - // buff_status will be clear by the next run - } else { - ep->next_pid ^= 1u; // roll back pid if aborted - } + #if CFG_TUD_ENABLED + if (!is_host) { + io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); + const uint32_t abort_bit = TU_BIT(tu_edpt_number(ep->ep_addr) << 1); // IN endpoint - *buf_reg = 0; // reset buffer control + #if CFG_TUSB_RP2_ERRATA_E2 + if (rp2040_chipversion >= 2) + #endif + { + usb_hw_set->abort = abort_bit; + while ((usb_hw->abort_done & abort_bit) != abort_bit) {} + } - #if CFG_TUSB_RP2_ERRATA_E2 - if (rp2040_chipversion >= 2) - #endif - { - usb_hw_clear->abort_done = abort_bit; - usb_hw_clear->abort = abort_bit; + // After abort, check if the other buffer received valid data + const uint16_t buf_ctrl16_other = *buf_reg16_other; + if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { + // Host already sent data into this buffer (e.g. write payload right after short CBW). + // Save it for the next transfer. + ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); + ep->future_bufid = buf_id ^ 1; + // buff_status will be clear by the next run + } else { + ep->next_pid ^= 1u; // roll back pid if aborted + } + + *buf_reg = 0; // reset buffer control + + #if CFG_TUSB_RP2_ERRATA_E2 + if (rp2040_chipversion >= 2) + #endif + { + usb_hw_clear->abort_done = abort_bit; + usb_hw_clear->abort = abort_bit; + } } + #endif hw_endpoint_lock_update(ep, -1); return true; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 3b45c7c94..c4c7e625e 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -145,8 +145,8 @@ TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_exit(void) { //--------------------------------------------------------------------+ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); -bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id); -void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg); +bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id, bool is_rx); +void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx, bool force_single); void rp2usb_reset_transfer(hw_endpoint_t *ep); @@ -161,7 +161,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct //--------------------------------------------------------------------+ void bufctrl_write32(io_rw_32 *buf_reg, uint32_t value); void bufctrl_write16(io_rw_16 *buf_reg16, uint16_t value); -uint16_t bufctrl_prepare16(struct hw_endpoint *ep, uint8_t *dpram_buf, bool is_rx); +uint16_t bufctrl_prepare16(hw_endpoint_t *ep, uint8_t *dpram_buf, bool is_rx); TU_ATTR_ALWAYS_INLINE static inline uintptr_t hw_data_offset(uint8_t *buf) { // Remove usb base from buffer pointer -- cgit v1.3.1 From 31ec5a7d3afdb79c95cfc6eeaf9659066d83fd69 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 30 Mar 2026 12:19:20 +0700 Subject: hcd rp2 add double buffered for control endpoint transfers. --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 42 ++++++++++------------------ src/portable/raspberrypi/rp2040/rp2040_usb.c | 34 +++++++++++----------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 7 +++++ 3 files changed, 39 insertions(+), 44 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 31bbfc0be..3a99e7275 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -55,12 +55,6 @@ static hw_endpoint_t ep_pool[USB_MAX_ENDPOINTS]; static hw_endpoint_t *epx = &ep_pool[0]; // current active endpoint -// Flags we set by default in sie_ctrl (we add other bits on top) -enum { - SIE_CTRL_BASE = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS, - SIE_CTRL_BASE_MASK = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS | USB_SIE_CTRL_SOF_EN_BITS | - USB_SIE_CTRL_KEEP_ALIVE_EN_BITS -}; enum { SIE_CTRL_SPEED_DISCONNECT = 0, @@ -187,7 +181,7 @@ static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; epx_ctrl_prepare(ep); - rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, is_rx || ep->transfer_type == TUSB_XFER_INTERRUPT); + rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, ep->transfer_type == TUSB_XFER_INTERRUPT); usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); sie_start_xfer(false, is_rx, ep->need_pre); // start transfer @@ -340,26 +334,20 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // RP2040: on SOF, stop and switch if there's a pending ep if (status & USB_INTS_HOST_SOF_BITS) { (void)usb_hw->sof_rd; // clear SOF by reading SOF_RD - if (epx->active && tu_edpt_number(epx->ep_addr) != 0) { - hw_endpoint_t *next_ep = epx_next_pending(epx); - if (next_ep) { + hw_endpoint_t *next_ep = epx_next_pending(epx); + if (next_ep == NULL) { + // no more pending --> disable SOF + usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; + usb_hw->nak_poll = USB_NAK_POLL_RESET; + } else { + // stop transfer if is active + if (epx->active) { usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} - busy_wait_at_least_cycles(12); - if (usb_hw->buf_status & 1u) { - usb_hw->nak_poll = USB_NAK_POLL_RESET; - handle_buf_status_isr(); - } else { - epx_switch_ep(next_ep); - } - } else { - usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; - usb_hw->nak_poll = USB_NAK_POLL_RESET; } - } else if (!epx_next_pending(epx)) { - // EPX is on control endpoint or inactive — disable SOF if nothing pending - usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; - usb_hw->nak_poll = USB_NAK_POLL_RESET; + + epx_save_context(); + epx_switch_ep(next_ep); } } #endif @@ -602,10 +590,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; #else // Only enable SOF round-robin for non-control endpoints - if (tu_edpt_number(epx->ep_addr) != 0) { - usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | (300 << USB_NAK_POLL_DELAY_LS_LSB); - usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; - } + usb_hw->nak_poll = (300 << USB_NAK_POLL_DELAY_FS_LSB) | (300 << USB_NAK_POLL_DELAY_LS_LSB); + usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; #endif } else { const uint8_t ep_num = tu_edpt_number(ep->ep_addr); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 49c948af6..ffb5fcbc6 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -283,7 +283,7 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u const bool is_rx = (is_host == (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN)); #if CFG_TUH_ENABLED - const bool force_single = (is_host && (is_rx || ep->transfer_type == TUSB_XFER_INTERRUPT)); + const bool force_single = (is_host && ep->transfer_type == TUSB_XFER_INTERRUPT); #else const bool force_single = false; #endif @@ -339,23 +339,17 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ return false; } - const bool is_double = (ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS)); const bool is_host = rp2usb_is_host_mode(); - - #if CFG_TUSB_RP2_ERRATA_E4 - const bool need_e4_fix = (is_host && !is_double); - #endif + const bool is_double = (ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS)); // Double-buffered: buf_id from BUFF_CPU_SHOULD_HANDLE indicates which buffer completed. - // RP2040-E4 (host only): in single-buffered multi-packet transfers, the controller may write completion status to - // BUF1 half instead of BUF0. The side effect that controller can execute an extra packet after writing to BUF1 - // since it leave BUF0 intact, which can be poll before buf_status interrupt is trigger. - // Workaround for the side effect, we will enable double-buffered for rx but only prepare 1 buf at a time. + // BUF1 half instead of BUF0. The side effect is that controller can execute an extra packet after writing to BUF1 + // since it leaves BUF0 intact, which can be polled before buf_status interrupt is triggered. uint8_t *dpram_buf = ep->dpram_buf; if (buf_id) { #if CFG_TUSB_RP2_ERRATA_E4 - if (!need_e4_fix) // incorrect buf_id, buffer pointer is still buf0 + if (!(is_host && !is_double)) // incorrect buf_id, buffer data is still buf0 #endif { dpram_buf += 64; // buf1 offset @@ -368,16 +362,24 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ const uint16_t xact_bytes = bufctrl_sync16(ep, is_rx, buf_ctrl16, dpram_buf); const bool is_last = buf_ctrl16 & USB_BUF_CTRL_LAST; const bool is_short = xact_bytes < ep->max_packet_size; - const bool is_done = is_short || (buf_ctrl16 & USB_BUF_CTRL_LAST); + const bool is_done = is_short || is_last; - // Short packet on rx with double buffer: abort the other half (if not last) and reset double-buffer state. + // Short packet on rx with double buffer: abort the other half (if not last) and reset the buffer control. // The other buffer may be: (a) still AVAIL, (b) in-progress (controller receiving), or (c) already completed. // We must abort to safely reclaim it. If it has valid data (FULL), save as future for the next transfer. - // After abort, zero buf_ctrl. - // Note: Host mode we cannot save next transfer data due to shared epx --> force single + // Note: Host mode current does not save next transfer data due to shared epx --> potential issue. However, RP2040-E4 + // causes more or less of the same issue since it write to buf1 and next time it continues to transfer on buf0 (stale) if (is_short && is_double && is_rx && !is_last) { #if CFG_TUH_ENABLED - if (is_host) {} + if (is_host) { + // stop current transfer + uint32_t sie_ctrl = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; + sie_ctrl |= USB_SIE_CTRL_STOP_TRANS_BITS; + usb_hw->sie_ctrl = sie_ctrl; + // maybe wait until STOP_TRANS bit is clear + + *buf_reg = 0; // reset buffer control + } #endif #if CFG_TUD_ENABLED diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index c4c7e625e..2ba9d018e 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -70,6 +70,13 @@ #define __tusb_irq_path_func(x) x #endif +// Flags we set by default in sie_ctrl (we add other bits on top) +enum { + SIE_CTRL_BASE = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS, + SIE_CTRL_BASE_MASK = USB_SIE_CTRL_PULLDOWN_EN_BITS | USB_SIE_CTRL_EP0_INT_1BUF_BITS | USB_SIE_CTRL_SOF_EN_BITS | + USB_SIE_CTRL_KEEP_ALIVE_EN_BITS +}; + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -- cgit v1.3.1 From 660c8ca087c8006433301d9208143438f5f456a1 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 31 Mar 2026 16:01:58 +0700 Subject: host abort transfer on short packet in double buffer. --- src/portable/raspberrypi/rp2040/rp2040_usb.c | 65 +++++++++++++--------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index ffb5fcbc6..21237b059 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -102,6 +102,7 @@ void rp2usb_init(void) { void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) { ep->active = false; + ep->pending = 0; ep->remaining_len = 0; ep->xferred_len = 0; ep->user_buf = 0; @@ -172,9 +173,8 @@ uint16_t __tusb_irq_path_func(bufctrl_prepare16)(hw_endpoint_t *ep, uint8_t *dpr buf_ctrl |= USB_BUF_CTRL_FULL; } - // Is this the last buffer? Only really matters for host mode. Will trigger - // the trans complete irq but also stop it polling. We only really care about - // trans complete for setup packets being sent + // Is this the last buffer? Will trigger the trans complete irq but also stop it polling. + // This is used to detect setup packets being sent in host mode if (ep->remaining_len == 0) { buf_ctrl |= USB_BUF_CTRL_LAST; } @@ -349,7 +349,7 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ uint8_t *dpram_buf = ep->dpram_buf; if (buf_id) { #if CFG_TUSB_RP2_ERRATA_E4 - if (!(is_host && !is_double)) // incorrect buf_id, buffer data is still buf0 + if (!(is_host && !is_double)) // E4 bug: incorrect buf_id, buffer data is still buf0 #endif { dpram_buf += 64; // buf1 offset @@ -370,54 +370,51 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ // Note: Host mode current does not save next transfer data due to shared epx --> potential issue. However, RP2040-E4 // causes more or less of the same issue since it write to buf1 and next time it continues to transfer on buf0 (stale) if (is_short && is_double && is_rx && !is_last) { - #if CFG_TUH_ENABLED + const uint32_t abort_bit = TU_BIT(tu_edpt_number(ep->ep_addr) << 1); // abort is device only -> IN endpoint + if (is_host) { - // stop current transfer - uint32_t sie_ctrl = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; - sie_ctrl |= USB_SIE_CTRL_STOP_TRANS_BITS; + // host stop current transfer, not safe, can be racing + const uint32_t sie_ctrl = (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK) | USB_SIE_CTRL_STOP_TRANS_BITS; usb_hw->sie_ctrl = sie_ctrl; - // maybe wait until STOP_TRANS bit is clear - - *buf_reg = 0; // reset buffer control - } - #endif - - #if CFG_TUD_ENABLED - if (!is_host) { - io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); - const uint32_t abort_bit = TU_BIT(tu_edpt_number(ep->ep_addr) << 1); // IN endpoint - - #if CFG_TUSB_RP2_ERRATA_E2 + while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} + } else { + // device abort current transfer + #if CFG_TUSB_RP2_ERRATA_E2 if (rp2040_chipversion >= 2) - #endif + #endif { usb_hw_set->abort = abort_bit; while ((usb_hw->abort_done & abort_bit) != abort_bit) {} } + } - // After abort, check if the other buffer received valid data - const uint16_t buf_ctrl16_other = *buf_reg16_other; - if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { - // Host already sent data into this buffer (e.g. write payload right after short CBW). - // Save it for the next transfer. - ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); - ep->future_bufid = buf_id ^ 1; - // buff_status will be clear by the next run + // After abort, check if the other buffer received valid data + io_rw_16 *buf_reg16_other = buf_reg16 + (buf_id ^ 1); + const uint16_t buf_ctrl16_other = *buf_reg16_other; + if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { + // Data already sent into this buffer. Save it for the next transfer. + // buff_status will be clear by the next run + if (is_host) { + // host put future_len pointer at end of epx_data } else { - ep->next_pid ^= 1u; // roll back pid if aborted + ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); } + ep->future_bufid = buf_id ^ 1; + } else { + ep->next_pid ^= 1u; // roll back pid if aborted + } - *buf_reg = 0; // reset buffer control + *buf_reg = 0; // reset buffer control - #if CFG_TUSB_RP2_ERRATA_E2 + if (!is_host) { + #if CFG_TUSB_RP2_ERRATA_E2 if (rp2040_chipversion >= 2) - #endif + #endif { usb_hw_clear->abort_done = abort_bit; usb_hw_clear->abort = abort_bit; } } - #endif hw_endpoint_lock_update(ep, -1); return true; -- cgit v1.3.1 From 9ac343a0471b03b1a76166c72347e7fdbf2c4800 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 31 Mar 2026 17:41:50 +0700 Subject: finally get rp2040 host epx working with 2-sof solution for switching --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 2 +- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 314 +++++++++++++++------------ src/portable/raspberrypi/rp2040/rp2040_usb.c | 2 +- test/hil/hil_test.py | 11 +- 4 files changed, 189 insertions(+), 140 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index d4c1bf708..8814f95d1 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -164,7 +164,7 @@ static void __tusb_irq_path_func(handle_hw_buff_status)(void) { while (buf_status) { // ctz/clz is faster than loop which has only a few bit set in general const uint8_t i = (uint8_t) __builtin_ctz(buf_status); - const uint bit = TU_BIT(i); + const uint32_t bit = TU_BIT(i); // IN transfer for even i, OUT transfer for odd i const uint8_t epnum = i >> 1u; diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 3a99e7275..fb1676f50 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -1,3 +1,4 @@ + /* * The MIT License (MIT) * @@ -29,23 +30,23 @@ #if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB && !CFG_TUH_MAX3421 -#include "pico.h" + #include "pico.h" -#if defined(PICO_RP2350) && PICO_RP2350 == 1 -#define HAS_STOP_EPX_ON_NAK -#endif + #if defined(PICO_RP2350) && PICO_RP2350 == 1 + #define HAS_STOP_EPX_ON_NAK + #endif // port 0 is native USB port, other is counted as software PIO #define RHPORT_NATIVE 0 -//--------------------------------------------------------------------+ + //--------------------------------------------------------------------+ // INCLUDE -//--------------------------------------------------------------------+ -#include "rp2040_usb.h" -#include "osal/osal.h" + //--------------------------------------------------------------------+ + #include "rp2040_usb.h" + #include "osal/osal.h" -#include "host/hcd.h" -#include "host/usbh.h" + #include "host/hcd.h" + #include "host/usbh.h" //--------------------------------------------------------------------+ // @@ -55,6 +56,9 @@ static hw_endpoint_t ep_pool[USB_MAX_ENDPOINTS]; static hw_endpoint_t *epx = &ep_pool[0]; // current active endpoint + #ifndef HAS_STOP_EPX_ON_NAK +static volatile bool epx_switch_request = false; + #endif enum { SIE_CTRL_SPEED_DISCONNECT = 0, @@ -67,7 +71,7 @@ enum { //--------------------------------------------------------------------+ static hw_endpoint_t *edpt_alloc(void) { - for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { + for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++) { hw_endpoint_t *ep = &ep_pool[i]; if (ep->max_packet_size == 0) { return ep; @@ -89,11 +93,11 @@ static hw_endpoint_t *edpt_find(uint8_t daddr, uint8_t ep_addr) { } TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *dpram_int_ep_ctrl(uint8_t int_num) { - return &usbh_dpram->int_ep_ctrl[int_num-1].ctrl; + return &usbh_dpram->int_ep_ctrl[int_num - 1].ctrl; } -TU_ATTR_ALWAYS_INLINE static inline io_rw_32 * dpram_int_ep_buffer_ctrl(uint8_t int_num) { - return &usbh_dpram->int_ep_buffer_ctrl[int_num-1].ctrl; +TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *dpram_int_ep_buffer_ctrl(uint8_t int_num) { + return &usbh_dpram->int_ep_buffer_ctrl[int_num - 1].ctrl; } //--------------------------------------------------------------------+ @@ -113,30 +117,41 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) { //--------------------------------------------------------------------+ // EPX //--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline void sie_stop_xfer(void) { + uint32_t sie_ctrl = (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK) | USB_SIE_CTRL_STOP_TRANS_BITS; + usb_hw->sie_ctrl = sie_ctrl; + while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} +} + TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, bool is_rx, bool need_pre) { - uint32_t value = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits + uint32_t sie_ctrl = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits if (send_setup) { - value |= USB_SIE_CTRL_SEND_SETUP_BITS; + sie_ctrl |= USB_SIE_CTRL_SEND_SETUP_BITS; } else { - value |= (is_rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); + sie_ctrl |= (is_rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS); } if (need_pre) { - value |= USB_SIE_CTRL_PREAMBLE_EN_BITS; + sie_ctrl |= USB_SIE_CTRL_PREAMBLE_EN_BITS; } // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". // We write everything except the START_TRANS bit first, then wait some cycles. - usb_hw->sie_ctrl = value; + usb_hw->sie_ctrl = sie_ctrl; busy_wait_at_least_cycles(12); - usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS; + usb_hw->sie_ctrl = sie_ctrl | USB_SIE_CTRL_START_TRANS_BITS; +} + +TU_ATTR_ALWAYS_INLINE static inline void epx_start_xfer(hw_endpoint_t *ep, bool is_setup) { + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (tu_edpt_number(ep->ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB)); + sie_start_xfer(is_setup, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, ep->need_pre); } // prepare epx_ctrl register for new endpoint TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(hw_endpoint_t *ep) { - // RP2040-E4: USB host writes status to upper half of buffer control in single buffered mode. + // RP2040-E4: USB host writes status to the upper half of buffer control in single buffered mode. // The buffer selector toggles even in single-buffered mode, so the previous transfer's status - // may have been written to BUF1 half, leaving BUF0 with stale AVAILABLE bit. Clear it here. + // may have been written to BUF1 half, leaving BUF0 with a stale AVAILABLE bit. Clear it here. #if defined(PICO_RP2040) && PICO_RP2040 == 1 usbh_dpram->epx_buf_ctrl = 0; #endif @@ -147,23 +162,46 @@ TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(hw_endpoint_t *ep) { usbh_dpram->epx_ctrl = ep_ctrl; } -// save on-going context -static void __tusb_irq_path_func(epx_save_context)(void) { - const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; - const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK; // TODO handle double buffered case - epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len); - epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0; - if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) { - epx->user_buf -= buf0_len; - } - epx->pending = 1; - epx->active = false; +// Save buffer context for EPX preemption (called after STOP_TRANS). +// Undo PID toggle and buffer accounting for buffers NOT completed on the wire. +// A buffer completed on wire means: controller reached STATUS phase (ACK received). +// OUT completed: FULL cleared to 0 in STATUS phase (was 1 when armed) +// IN completed: FULL set to 1 in STATUS phase (was 0 when armed) +// So undo when: AVAIL=1 (never started), or (OUT: FULL=1) or (IN: FULL=0) +static void __tusb_irq_path_func(epx_save_context)(hw_endpoint_t *ep) { + uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl; + const bool is_out = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_OUT); + + do { + const uint16_t bc16 = (uint16_t)buf_ctrl; + if (bc16) { + const bool avail = (bc16 & USB_BUF_CTRL_AVAIL); + const bool full = (bc16 & USB_BUF_CTRL_FULL); + if (avail || (is_out ? full : !full)) { + const uint16_t buf_len = bc16 & USB_BUF_CTRL_LEN_MASK; + ep->remaining_len += buf_len; + ep->next_pid ^= 1u; + if (is_out) { + ep->user_buf -= buf_len; + } + } + } + + if (usbh_dpram->epx_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS) { + buf_ctrl >>= 16; + } else { + buf_ctrl = 0; + } + } while (buf_ctrl > 0); usbh_dpram->epx_buf_ctrl = 0; + + ep->pending = 1; + ep->active = false; } // All non-interrupt endpoints use shared EPX. -// Save current EPX context, mark pending, switch to ep +// Save the current EPX context, mark pending, switch to ep static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { const bool is_setup = (ep->pending == 2); @@ -172,19 +210,17 @@ static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { ep->active = true; if (is_setup) { - usb_hw->dev_addr_ctrl = ep->dev_addr; - sie_start_xfer(true, false, ep->need_pre); + // panic("new setup \n"); + epx_start_xfer(ep, true); } else { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const bool is_rx = tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN; - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; - io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; epx_ctrl_prepare(ep); - rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, ep->transfer_type == TUSB_XFER_INTERRUPT); + rp2usb_buffer_start(ep, ep_reg, buf_reg, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, + ep->transfer_type == TUSB_XFER_INTERRUPT); - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - sie_start_xfer(false, is_rx, ep->need_pre); // start transfer + epx_start_xfer(ep, false); } } @@ -208,16 +244,14 @@ static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ //--------------------------------------------------------------------+ // Interrupt handlers //--------------------------------------------------------------------+ -static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_result_t xfer_result) { +static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_result_t xfer_result, bool is_more) { // Mark transfer as done before we tell the tinyusb stack - uint8_t dev_addr = ep->dev_addr; - uint8_t ep_addr = ep->ep_addr; - uint xferred_len = ep->xferred_len; + uint xferred_len = ep->xferred_len; rp2usb_reset_transfer(ep); - hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true); + hcd_event_xfer_complete(ep->dev_addr, ep->ep_addr, xferred_len, xfer_result, true); // Carry more transfer on epx - if (ep == epx) { + if (is_more) { hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep != NULL) { epx_switch_ep(next_ep); @@ -231,26 +265,31 @@ static void __tusb_irq_path_func(handle_buf_status_isr)(void) { BUF_STATUS_EPX = 1u }; - // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0.3 + // Check EPX first (bit 0). // Double-buffered: if both buffers completed at once, buf_status re-sets // immediately after clearing (datasheet Table 406). Process the second buffer too. while (usb_hw->buf_status & BUF_STATUS_EPX) { - const uint8_t buf_id = (usb_hw->buf_cpu_should_handle & BUF_STATUS_EPX) ? 1 : 0; + const uint8_t buf_id = (usb_hw->buf_cpu_should_handle & BUF_STATUS_EPX) ? 1 : 0; usb_hw_clear->buf_status = 1u; // clear io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + #ifndef HAS_STOP_EPX_ON_NAK + // Any packet completion (mid-transfer or final) means data is flowing. + // Clear switch request so the 2-SOF fallback only fires for NAK-retrying endpoints. + epx_switch_request = false; + #endif if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id, tu_edpt_dir(epx->ep_addr) == TUSB_DIR_IN)) { - xfer_complete_isr(epx, XFER_RESULT_SUCCESS); + xfer_complete_isr(epx, XFER_RESULT_SUCCESS, true); } } // Check "interrupt" (asynchronous) endpoints for both IN and OUT - uint32_t buf_status = usb_hw->buf_status & (uint32_t)~BUF_STATUS_EPX; + uint32_t buf_status = usb_hw->buf_status & ~(uint32_t)BUF_STATUS_EPX; while (buf_status) { // ctz/clz is faster than loop which has only a few bit set in general - const uint8_t idx = (uint8_t) __builtin_ctz(buf_status); - const uint bit = TU_BIT(idx); + const uint8_t idx = (uint8_t)__builtin_ctz(buf_status); + const uint32_t bit = TU_BIT(idx); usb_hw_clear->buf_status = bit; buf_status &= ~bit; @@ -258,7 +297,7 @@ static void __tusb_irq_path_func(handle_buf_status_isr)(void) { // EPX is bit 0. Bit 1 is not used // IEP1 IN/OUT is bit 2, 3 // IEP2 IN/OUT is bit 4, 5 etc - const uint8_t epnum = idx >> 1u; + const uint8_t epnum = idx >> 1u; for (size_t e = 0; e < TU_ARRAY_SIZE(ep_pool); e++) { hw_endpoint_t *ep = &ep_pool[e]; if (ep->interrupt_num == epnum) { @@ -266,7 +305,7 @@ static void __tusb_irq_path_func(handle_buf_status_isr)(void) { io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN); if (done) { - xfer_complete_isr(ep, XFER_RESULT_SUCCESS); + xfer_complete_isr(ep, XFER_RESULT_SUCCESS, false); } break; } @@ -293,69 +332,80 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { } if (status & USB_INTS_STALL_BITS) { - // We have rx'd a stall from the device - // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS - // AND TRANS_COMPLETE as the stall is an alternative response - // to one of those events usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS; - xfer_complete_isr(epx, XFER_RESULT_STALLED); + xfer_complete_isr(epx, XFER_RESULT_STALLED, true); } - if (status & USB_INTS_BUFF_STATUS_BITS) { - handle_buf_status_isr(); + if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { + usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; + + const uint32_t sie_ctrl = (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK) | USB_SIE_CTRL_STOP_TRANS_BITS; + usb_hw->sie_ctrl = sie_ctrl; + // while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} + + // Even if STOP_TRANS bit is clear, controller maybe in middle of retrying and may re-raise timeout once extra time + // Only handle if epx is active, don't carry more epx transfer since STOP_TRANS is raced and not safe. + if (epx->active) { + xfer_complete_isr(epx, XFER_RESULT_FAILED, false); + } } if (status & USB_INTS_TRANS_COMPLETE_BITS) { + // only applies for epx, interrupt endpoint does not seem to raise this usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS; - - // only handle a setup packet if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) { - epx->xferred_len = 8; - xfer_complete_isr(epx, XFER_RESULT_SUCCESS); - } else { - // Don't care. Will handle this in buff status + uint32_t sie_ctrl = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; + usb_hw->sie_ctrl = sie_ctrl; // clear setup bit + epx->xferred_len = 8; + xfer_complete_isr(epx, XFER_RESULT_SUCCESS, true); } } + if (status & USB_INTS_BUFF_STATUS_BITS) { + handle_buf_status_isr(); + } + + // SOF-based round-robin MUST run BEFORE BUFF_STATUS to avoid processing + // buf_status on the wrong EPX after a completion+switch in handle_buf_status_isr. #ifdef HAS_STOP_EPX_ON_NAK if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) { usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS; hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep != NULL) { - epx_save_context(); + epx_save_context(epx); epx_switch_ep(next_ep); } else { - // No pending endpoint, this is the only active one: disable stop-on-NAK, continue current transfer usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; sie_start_xfer(false, TUSB_DIR_IN == tu_edpt_dir(epx->ep_addr), epx->need_pre); } } #else - // RP2040: on SOF, stop and switch if there's a pending ep + // RP2040: on SOF, switch EPX if another endpoint is pending. + // First SOF sets epx_switch_request. If a transfer completes before next SOF, the flag is + // cleared (data is flowing, no need to force-switch). Second SOF with flag still set means + // no data exchanged (endpoint NAK-retrying): STOP_TRANS is safe and we switch. + // This avoids stopping mid-data-transfer which corrupts double-buffered PID tracking. if (status & USB_INTS_HOST_SOF_BITS) { (void)usb_hw->sof_rd; // clear SOF by reading SOF_RD hw_endpoint_t *next_ep = epx_next_pending(epx); if (next_ep == NULL) { - // no more pending --> disable SOF usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; usb_hw->nak_poll = USB_NAK_POLL_RESET; - } else { - // stop transfer if is active - if (epx->active) { - usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS; - while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} + epx_switch_request = false; + } else if (epx->active) { + if (epx_switch_request) { + // Second SOF with no transfer completion: endpoint is NAK-retrying, safe to switch. + epx_switch_request = false; + sie_stop_xfer(); + epx_save_context(epx); + epx_switch_ep(next_ep); + } else { + epx_switch_request = true; } - - epx_save_context(); - epx_switch_ep(next_ep); } } #endif - if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS) { - usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS; - } - if (status & USB_INTS_ERROR_DATA_SEQ_BITS) { usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS; panic("Data Seq Error \n"); @@ -371,9 +421,9 @@ void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) { //--------------------------------------------------------------------+ // HCD API //--------------------------------------------------------------------+ -bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rhport; - (void) rh_init; +bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { + (void)rhport; + (void)rh_init; pico_trace("hcd_init %d\n", rhport); assert(rhport == 0); @@ -392,24 +442,20 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Enable in host mode with SOF / Keep alive on usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS; - usb_hw->sie_ctrl = SIE_CTRL_BASE; - usb_hw->inte = USB_INTE_BUFF_STATUS_BITS | - USB_INTE_HOST_CONN_DIS_BITS | - USB_INTE_HOST_RESUME_BITS | - USB_INTE_STALL_BITS | - USB_INTE_TRANS_COMPLETE_BITS | - USB_INTE_ERROR_RX_TIMEOUT_BITS | - USB_INTE_ERROR_DATA_SEQ_BITS ; - -#ifdef HAS_STOP_EPX_ON_NAK + usb_hw->sie_ctrl = SIE_CTRL_BASE; + usb_hw->inte = USB_INTE_BUFF_STATUS_BITS | USB_INTE_HOST_CONN_DIS_BITS | USB_INTE_HOST_RESUME_BITS | + USB_INTE_STALL_BITS | USB_INTE_TRANS_COMPLETE_BITS | USB_INTE_ERROR_RX_TIMEOUT_BITS | + USB_INTE_ERROR_DATA_SEQ_BITS; + + #ifdef HAS_STOP_EPX_ON_NAK usb_hw_set->inte = USB_INTE_EPX_STOPPED_ON_NAK_BITS; -#endif + #endif return true; } bool hcd_deinit(uint8_t rhport) { - (void) rhport; + (void)rhport; irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq); reset_block(RESETS_RESET_USBCTRL_BITS); unreset_block_wait(RESETS_RESET_USBCTRL_BITS); @@ -417,7 +463,7 @@ bool hcd_deinit(uint8_t rhport) { } void hcd_port_reset(uint8_t rhport) { - (void) rhport; + (void)rhport; // TODO: Nothing to do here yet. Perhaps need to reset some state? } @@ -445,7 +491,10 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) { // Close all opened endpoint belong to this device void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void)rhport; - (void)dev_addr; + + if (dev_addr == 0) { + return; // address 0 is for device enumeration + } // reset epx if it is currently active with unplugged device if (epx->max_packet_size > 0 && epx->dev_addr == dev_addr) { @@ -462,13 +511,13 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { if (ep->interrupt_num) { // disable interrupt endpoint - usb_hw_clear->int_ep_ctrl = 1u << ep->interrupt_num; + usb_hw_clear->int_ep_ctrl = TU_BIT(ep->interrupt_num); usb_hw->int_ep_addr_ctrl[ep->interrupt_num - 1] = 0; io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); - *buf_reg = 0; - *ep_reg = 0; + *buf_reg = 0; + *ep_reg = 0; } ep->max_packet_size = 0; // mark as unused @@ -498,13 +547,17 @@ void hcd_int_disable(uint8_t rhport) { bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc) { (void)rhport; pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress); - hw_endpoint_t *ep = edpt_alloc(); + hw_endpoint_t *ep; + if (dev_addr == 0) { + ep = &ep_pool[0]; + } else { + ep = edpt_alloc(); + } TU_ASSERT(ep); const uint8_t ep_addr = ep_desc->bEndpointAddress; const uint16_t max_packet_size = tu_edpt_packet_size(ep_desc); const uint8_t transfer_type = ep_desc->bmAttributes.xfer; - // const uint8_t bmInterval = ep_desc->bInterval; ep->max_packet_size = max_packet_size; ep->ep_addr = ep_addr; @@ -532,7 +585,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t ep->dpram_buf = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX - (int_idx + 1u) * 64u); uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | (TUSB_XFER_INTERRUPT << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf) | - (uint32_t)((ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); + ((uint32_t)(ep_desc->bInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); usbh_dpram->int_ep_ctrl[int_idx].ctrl = ep_ctrl; //------------- address control -------------// @@ -547,7 +600,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t usb_hw->int_ep_addr_ctrl[int_idx] = addr_ctrl; // Finally, activate interrupt endpoint - usb_hw_set->int_ep_ctrl = 1u << ep->interrupt_num; + usb_hw_set->int_ep_ctrl = TU_BIT(ep->interrupt_num); } return true; @@ -560,6 +613,14 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } +bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void)rhport; + (void)dev_addr; + (void)ep_addr; + // TODO not implemented yet + return false; +} + bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { (void)rhport; @@ -594,19 +655,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; #endif } else { - const uint8_t ep_num = tu_edpt_number(ep->ep_addr); - const tusb_dir_t ep_dir = tu_edpt_dir(ep->ep_addr); - const bool is_rx = (ep_dir == TUSB_DIR_IN); - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; - io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; epx = ep; epx_ctrl_prepare(ep); rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); // prepare bufctrl - - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB)); - sie_start_xfer(false, is_rx, ep->need_pre); // start transfer + epx_start_xfer(ep, false); } rp2usb_critical_exit(); } @@ -614,27 +670,19 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b return true; } -bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { - (void)rhport; - (void)dev_addr; - (void)ep_addr; - // TODO not implemented yet - return false; -} - bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { (void)rhport; - // Copy data into setup packet buffer (usbh only schedules one setup at a time) - for (uint8_t i = 0; i < 8; i++) { - usbh_dpram->setup_packet[i] = setup_packet[i]; - } - hw_endpoint_t *ep = edpt_find(dev_addr, 0x00); TU_ASSERT(ep); rp2usb_critical_enter(); + // Copy data into setup packet buffer (usbh only schedules one setup at a time) + for (uint8_t i = 0; i < 8; i++) { + usbh_dpram->setup_packet[i] = setup_packet[i]; + } + ep->ep_addr = 0; // setup is OUT ep->remaining_len = 8; ep->xferred_len = 0; @@ -651,9 +699,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet } else { epx = ep; ep->active = true; - - usb_hw->dev_addr_ctrl = dev_addr; // Set device address - sie_start_xfer(true, false, ep->need_pre); // start transfer + epx_start_xfer(ep, true); } rp2usb_critical_exit(); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 21237b059..156be62e4 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -197,7 +197,7 @@ void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_r ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS; } else { // Only buf0 used: clear DOUBLE_BUFFERED so controller doesn't toggle buffer selector - ep_ctrl &= ~EP_CTRL_DOUBLE_BUFFERED_BITS; + ep_ctrl &= ~(uint32_t)EP_CTRL_DOUBLE_BUFFERED_BITS; } *ep_reg = ep_ctrl; } diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 04dd4e2bc..cf3cff1a3 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -50,7 +50,7 @@ import ctypes from pymtp import MTP import string -ENUM_TIMEOUT = 10 +ENUM_TIMEOUT = 15 STATUS_OK = "\033[32mOK\033[0m" STATUS_FAILED = "\033[31mFailed\033[0m" @@ -766,10 +766,13 @@ def test_device_cdc_msc(board): assert ret.returncode == 0, f'dd read failed: {ret.stdout.decode()}' read_speed = parse_dd_speed(ret.stdout.decode()) - # Write back the same data to avoid corrupting the disk + # 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') - assert ret.returncode == 0, f'dd write failed: {ret.stdout.decode()}' - write_speed = parse_dd_speed(ret.stdout.decode()) + 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()) try: os.remove(tmp_file) -- cgit v1.3.1 From 78d34d5b6488d7129530461ce1865a33a946e698 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 1 Apr 2026 15:51:31 +0700 Subject: reduce code size, use state to replace active + pending --- .claude/commands/build-doc.md | 24 ++++++ .claude/commands/hil.md | 30 ++++++++ examples/host/msc_file_explorer/README.md | 105 ++++++++++++++++++++++++++ src/portable/raspberrypi/rp2040/dcd_rp2040.c | 13 ++-- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 106 ++++++++++++--------------- src/portable/raspberrypi/rp2040/rp2040_usb.c | 78 +++++++++----------- src/portable/raspberrypi/rp2040/rp2040_usb.h | 27 ++++--- tools/metrics.py | 5 +- 8 files changed, 266 insertions(+), 122 deletions(-) create mode 100644 .claude/commands/build-doc.md create mode 100644 .claude/commands/hil.md create mode 100644 examples/host/msc_file_explorer/README.md (limited to 'src/portable/raspberrypi') diff --git a/.claude/commands/build-doc.md b/.claude/commands/build-doc.md new file mode 100644 index 000000000..c9ad9f539 --- /dev/null +++ b/.claude/commands/build-doc.md @@ -0,0 +1,24 @@ +# build-doc + +Scan all example READMEs and build the Sphinx documentation. + +## Instructions + +1. Install docs dependencies: + ```bash + pip install -r docs/requirements.txt + ``` + +2. Build the docs from the repo root: + ```bash + sphinx-build -b html docs docs/_build + ``` + `conf.py` automatically scans all `examples/{device,host,dual}/*/README.md`, copies them into `docs/examples/`, and regenerates `examples.rst` with the toctree. + +3. Use a timeout of at least 60 seconds. + +4. After the build completes: + - Show the build output to the user. + - Report total warnings and errors. + - List which example READMEs were discovered and included. + - If there are errors, suggest fixes. diff --git a/.claude/commands/hil.md b/.claude/commands/hil.md new file mode 100644 index 000000000..2ba35ec22 --- /dev/null +++ b/.claude/commands/hil.md @@ -0,0 +1,30 @@ +# hil + +Run Hardware-in-the-Loop (HIL) tests on physical boards. + +## Arguments +- $ARGUMENTS: Optional flags (e.g. board name, extra args). If empty, runs all boards with default config. + +## Instructions + +1. Determine the HIL config file: + ```bash + HIL_CONFIG=$( (systemctl list-units --type=service --state=running 2>/dev/null; systemctl --user list-units --type=service --state=running 2>/dev/null) | grep -q 'actions\.runner' && echo tinyusb.json || echo local.json ) + ``` + Default is `local.json` for local development. + +2. Parse $ARGUMENTS: + - If $ARGUMENTS contains `-b BOARD_NAME`, run for that specific board only. + - If $ARGUMENTS is empty or has no `-b`, run for all boards in the config. + - Pass through any other flags (e.g. `-v` for verbose) directly to the command. + +3. Run the HIL test from the repo root directory: + - Specific board: `python test/hil/hil_test.py -b BOARD_NAME -B examples $HIL_CONFIG $EXTRA_ARGS` + - All boards: `python test/hil/hil_test.py -B examples $HIL_CONFIG $EXTRA_ARGS` + +4. Use a timeout of at least 20 minutes (600000ms). HIL tests take 2-5 minutes. NEVER cancel early. + +5. After the test completes: + - Show the test output to the user. + - Summarize pass/fail results per board. + - If there are failures, suggest re-running with `-v` flag for verbose output to help debug. diff --git a/examples/host/msc_file_explorer/README.md b/examples/host/msc_file_explorer/README.md new file mode 100644 index 000000000..e220bedea --- /dev/null +++ b/examples/host/msc_file_explorer/README.md @@ -0,0 +1,105 @@ +# MSC File Explorer + +This host example implements an interactive command-line file browser for USB Mass Storage devices. +When a USB flash drive is connected, the device is automatically mounted using FatFS and a shell-like +CLI is presented over the board's serial console. + +## Features + +- Automatic mount/unmount of USB storage devices +- FAT12/16/32 filesystem support via FatFS +- Interactive CLI with command history +- Read speed benchmarking with `dd` +- Support for up to 4 simultaneous USB storage devices (via hub) + +## Supported Commands + +| Command | Usage | Description | +|---------|--------------------|------------------------------------------------------| +| help | `help` | Print list of available commands | +| cat | `cat ` | Print file contents to the console | +| cd | `cd ` | Change current working directory | +| cp | `cp ` | Copy a file | +| dd | `dd [count]` | Read sectors and report speed (default 1024 sectors) | +| ls | `ls [dir]` | List directory contents | +| pwd | `pwd` | Print current working directory | +| mkdir | `mkdir ` | Create a directory | +| mv | `mv ` | Rename/move a file or directory | +| rm | `rm ` | Remove a file | + +## Build + +Build for a specific board using CMake (see [Getting Started](https://docs.tinyusb.org/en/latest/getting_started.html)): + +```bash +# Example: build for Raspberry Pi Pico +cmake -B build -DBOARD=raspberry_pi_pico -DFAMILY=rp2040 examples/host/msc_file_explorer +cmake --build build +``` + +## Usage + +1. Flash the firmware to your board. +2. Open a serial terminal (e.g. `minicom`, `screen`, `PuTTY`) at 115200 baud. +3. Plug a USB flash drive into the board's USB host port. +4. The device is auto-mounted and the prompt appears: + +``` +TinyUSB MSC File Explorer Example + +Device connected + Vendor : Kingston + Product : DataTraveler 2.0 + Rev : 1.0 + Capacity: 1.9 GB + +0:/> _ +``` + +### Browsing Files + +``` +0:/> ls +----a 1234 readme.txt +d---- 0 photos +d---- 0 docs + +0:/> cd photos +0:/photos> ls +----a 520432 vacation.jpg +----a 312088 family.png + +0:/> cat readme.txt +Hello from USB drive! +``` + +### Copying and Moving Files + +``` +0:/> cp readme.txt backup.txt +0:/> mv backup.txt docs/backup.txt +``` + +### Measuring Read Speed + +``` +0:/> dd +Reading 1024 sectors... + Data speed: 823 KB/s +``` + +### Multiple Devices + +When using a USB hub, multiple drives are mounted as `0:`, `1:`, etc. Use the drive prefix to +navigate between them: + +``` +0:/> cd 1: +1:/> ls +``` + +## Testing + +This example is part of the TinyUSB HIL (Hardware-in-the-Loop) test suite. The HIL test +automatically flashes, runs the example, and verifies MSC enumeration and file operations +against a known USB drive. diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 8814f95d1..ca03ebf8a 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -111,7 +111,6 @@ static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t // double buffered Bulk endpoint if (transfer_type == TUSB_XFER_BULK) { size *= 2u; - #if CFG_TUSB_RP2_ERRATA_E15 if (dir == TUSB_DIR_IN) { ep->e15_bulk_in = true; @@ -195,7 +194,7 @@ TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) { for (uint8_t dir = 0; dir < 2; dir++) { struct hw_endpoint *ep = hw_endpoint_get(0, dir); ep->next_pid = 1u; - if (ep->active) { + if (ep->state == EPSTATE_ACTIVE) { hw_endpoint_abort_xfer(ep); // Abort any pending transfer per USB specs } } @@ -254,12 +253,12 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF - if (ep->e15_bulk_in && ep->active) { + if (ep->e15_bulk_in && ep->state == EPSTATE_ACTIVE) { keep_sof_alive = true; hw_endpoint_lock_update(ep, 1); - if (ep->pending) { - ep->pending = 0; + if (ep->state == EPSTATE_PENDING) { + ep->state = EPSTATE_ACTIVE; io_rw_32 *buf_reg32 = get_buf_ctrl(i, TUSB_DIR_IN); io_rw_16 *buf_reg16 = (io_rw_16 *)buf_reg32; @@ -276,7 +275,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { if (buf0_idle && buf1_idle) { // both are idle, start fresh io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN); - rp2usb_buffer_start(ep, ep_reg, buf_reg32, false, false); + rp2usb_buffer_start(ep, ep_reg, buf_reg32, false); } else if (buf0_idle) { uint16_t buf0 = bufctrl_prepare16(ep, ep->dpram_buf, false); bufctrl_write16(buf_reg16, buf0); @@ -501,7 +500,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc) struct hw_endpoint *ep = hw_endpoint_get(epnum, dir); TU_ASSERT(ep->dpram_buf != NULL); // must be inited and allocated previously - if (ep->active) { + if (ep->state == EPSTATE_ACTIVE) { hw_endpoint_abort_xfer(ep); // abort any pending transfer } ep->max_packet_size = ep_desc->wMaxPacketSize; diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index fb1676f50..02a4e055e 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -66,6 +66,10 @@ enum { SIE_CTRL_SPEED_FULL = 2, }; +enum { + EPX_CTRL_DEFAULT = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | offsetof(usb_host_dpram_t, epx_data) +}; + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -123,7 +127,7 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_stop_xfer(void) { while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {} } -TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, bool is_rx, bool need_pre) { +static void __tusb_irq_path_func(sie_start_xfer)(bool send_setup, bool is_rx, bool need_pre) { uint32_t sie_ctrl = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits if (send_setup) { sie_ctrl |= USB_SIE_CTRL_SEND_SETUP_BITS; @@ -135,31 +139,16 @@ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, bool is } // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit - // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access". + // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access".! // We write everything except the START_TRANS bit first, then wait some cycles. usb_hw->sie_ctrl = sie_ctrl; busy_wait_at_least_cycles(12); usb_hw->sie_ctrl = sie_ctrl | USB_SIE_CTRL_START_TRANS_BITS; } -TU_ATTR_ALWAYS_INLINE static inline void epx_start_xfer(hw_endpoint_t *ep, bool is_setup) { - usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (tu_edpt_number(ep->ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB)); - sie_start_xfer(is_setup, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, ep->need_pre); -} - // prepare epx_ctrl register for new endpoint -TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(hw_endpoint_t *ep) { - // RP2040-E4: USB host writes status to the upper half of buffer control in single buffered mode. - // The buffer selector toggles even in single-buffered mode, so the previous transfer's status - // may have been written to BUF1 half, leaving BUF0 with a stale AVAILABLE bit. Clear it here. - #if defined(PICO_RP2040) && PICO_RP2040 == 1 - usbh_dpram->epx_buf_ctrl = 0; - #endif - - // ep control - const uint32_t ep_ctrl = EP_CTRL_ENABLE_BITS | EP_CTRL_INTERRUPT_PER_BUFFER | - ((uint32_t)ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf); - usbh_dpram->epx_ctrl = ep_ctrl; +TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(uint8_t transfer_type) { + usbh_dpram->epx_ctrl = EPX_CTRL_DEFAULT | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB); } // Save buffer context for EPX preemption (called after STOP_TRANS). @@ -196,31 +185,30 @@ static void __tusb_irq_path_func(epx_save_context)(hw_endpoint_t *ep) { usbh_dpram->epx_buf_ctrl = 0; - ep->pending = 1; - ep->active = false; + ep->state = EPSTATE_PENDING; } -// All non-interrupt endpoints use shared EPX. -// Save the current EPX context, mark pending, switch to ep +// switch epx to new endpoint and start the transfer static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { - const bool is_setup = (ep->pending == 2); + const bool is_setup = (ep->state == EPSTATE_PENDING_SETUP); - epx = ep; // switch pointer - ep->pending = 0; - ep->active = true; + epx = ep; // switch pointer + ep->state = EPSTATE_ACTIVE; if (is_setup) { // panic("new setup \n"); - epx_start_xfer(ep, true); + usb_hw->dev_addr_ctrl = ep->dev_addr; + sie_start_xfer(true, false, ep->need_pre); } else { - io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; + const bool is_rx = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN); + io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl; io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl; - epx_ctrl_prepare(ep); - rp2usb_buffer_start(ep, ep_reg, buf_reg, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, - ep->transfer_type == TUSB_XFER_INTERRUPT); + epx_ctrl_prepare(ep->transfer_type); + rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx); - epx_start_xfer(ep, false); + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (tu_edpt_number(ep->ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB)); + sie_start_xfer(is_setup, is_rx, ep->need_pre); } } @@ -228,12 +216,12 @@ static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *ep) { static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ep) { const uint cur_idx = (uint)(cur_ep - &ep_pool[0]); for (uint i = cur_idx + 1; i < TU_ARRAY_SIZE(ep_pool); i++) { - if (ep_pool[i].pending) { + if (ep_pool[i].state >= EPSTATE_PENDING) { return &ep_pool[i]; } } for (uint i = 0; i < cur_idx; i++) { - if (ep_pool[i].pending) { + if (ep_pool[i].state >= EPSTATE_PENDING) { return &ep_pool[i]; } } @@ -246,7 +234,7 @@ static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ //--------------------------------------------------------------------+ static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_result_t xfer_result, bool is_more) { // Mark transfer as done before we tell the tinyusb stack - uint xferred_len = ep->xferred_len; + uint32_t xferred_len = ep->xferred_len; rp2usb_reset_transfer(ep); hcd_event_xfer_complete(ep->dev_addr, ep->ep_addr, xferred_len, xfer_result, true); @@ -345,7 +333,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { // Even if STOP_TRANS bit is clear, controller maybe in middle of retrying and may re-raise timeout once extra time // Only handle if epx is active, don't carry more epx transfer since STOP_TRANS is raced and not safe. - if (epx->active) { + if (epx->state == EPSTATE_ACTIVE) { xfer_complete_isr(epx, XFER_RESULT_FAILED, false); } } @@ -392,7 +380,7 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) { usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS; usb_hw->nak_poll = USB_NAK_POLL_RESET; epx_switch_request = false; - } else if (epx->active) { + } else if (epx->state == EPSTATE_ACTIVE) { if (epx_switch_request) { // Second SOF with no transfer completion: endpoint is NAK-retrying, safe to switch. epx_switch_request = false; @@ -496,20 +484,14 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { return; // address 0 is for device enumeration } - // reset epx if it is currently active with unplugged device - if (epx->max_packet_size > 0 && epx->dev_addr == dev_addr) { - // if (epx->active) { - // // need to abort transfer - // } - epx->max_packet_size = 0; - } + rp2usb_critical_enter(); for (size_t i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) { hw_endpoint_t *ep = &ep_pool[i]; if (ep->dev_addr == dev_addr && ep->max_packet_size > 0) { - ep->pending = 0; // clear any pending transfer + ep->state = EPSTATE_IDLE; // clear any pending transfer - if (ep->interrupt_num) { + if (ep->interrupt_num > 0) { // disable interrupt endpoint usb_hw_clear->int_ep_ctrl = TU_BIT(ep->interrupt_num); usb_hw->int_ep_addr_ctrl[ep->interrupt_num - 1] = 0; @@ -523,6 +505,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { ep->max_packet_size = 0; // mark as unused } } + + rp2usb_critical_exit(); } uint32_t hcd_frame_number(uint8_t rhport) { @@ -557,16 +541,15 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t const uint8_t ep_addr = ep_desc->bEndpointAddress; const uint16_t max_packet_size = tu_edpt_packet_size(ep_desc); - const uint8_t transfer_type = ep_desc->bmAttributes.xfer; ep->max_packet_size = max_packet_size; ep->ep_addr = ep_addr; ep->dev_addr = dev_addr; - ep->transfer_type = transfer_type; + ep->transfer_type = ep_desc->bmAttributes.xfer; ep->need_pre = need_pre(dev_addr); ep->next_pid = 0u; - if (transfer_type != TUSB_XFER_INTERRUPT) { + if (ep->transfer_type != TUSB_XFER_INTERRUPT) { ep->dpram_buf = usbh_dpram->epx_data; } else { // from 15 interrupt endpoints pool @@ -627,7 +610,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b hw_endpoint_t *ep = edpt_find(dev_addr, ep_addr); TU_ASSERT(ep); - if (ep->transfer_type == TUSB_XFER_INTERRUPT) { + if (ep->interrupt_num > 0) { // For interrupt endpoint control and buffer is already configured // Note: Interrupt is single buffered only io_rw_32 *ep_reg = dpram_int_ep_ctrl(ep->interrupt_num); @@ -642,10 +625,10 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b // If EPX is busy with another transfer, mark as pending rp2usb_critical_enter(); - if (epx->active) { + if (epx->state == EPSTATE_ACTIVE) { ep->user_buf = buffer; ep->remaining_len = buflen; - ep->pending = 1; + ep->state = EPSTATE_PENDING; #ifdef HAS_STOP_EPX_ON_NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; @@ -660,9 +643,10 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b epx = ep; - epx_ctrl_prepare(ep); + epx_ctrl_prepare(ep->transfer_type); rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); // prepare bufctrl - epx_start_xfer(ep, false); + usb_hw->dev_addr_ctrl = (uint32_t)(ep->dev_addr | (tu_edpt_number(ep->ep_addr) << USB_ADDR_ENDP_ENDPOINT_LSB)); + sie_start_xfer(false, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, ep->need_pre); } rp2usb_critical_exit(); } @@ -688,8 +672,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet ep->xferred_len = 0; // If EPX is busy, mark as pending setup (DPRAM already has the packet) - if (epx->active) { - ep->pending = 2; // setup + if (epx->state == EPSTATE_ACTIVE) { + ep->state = EPSTATE_PENDING_SETUP; #ifdef HAS_STOP_EPX_ON_NAK usb_hw_set->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS; #else @@ -697,9 +681,11 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet usb_hw_set->inte = USB_INTE_HOST_SOF_BITS; #endif } else { - epx = ep; - ep->active = true; - epx_start_xfer(ep, true); + epx = ep; + ep->state = EPSTATE_ACTIVE; + + usb_hw->dev_addr_ctrl = ep->dev_addr; + sie_start_xfer(true, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, ep->need_pre); } rp2usb_critical_exit(); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 156be62e4..206da041b 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -101,12 +101,13 @@ void rp2usb_init(void) { } void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) { - ep->active = false; - ep->pending = 0; + ep->state = EPSTATE_IDLE; ep->remaining_len = 0; ep->xferred_len = 0; ep->user_buf = 0; +#if CFG_TUD_EDPT_DEDICATED_HWFIFO ep->is_xfer_fifo = false; +#endif } void __tusb_irq_path_func(bufctrl_write32)(io_rw_32 *buf_reg, uint32_t value) { @@ -183,14 +184,19 @@ uint16_t __tusb_irq_path_func(bufctrl_prepare16)(hw_endpoint_t *ep, uint8_t *dpr } // Start transaction on hw buffer -void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx, - bool force_single) { +void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx) { // always compute and start with buffer 0 uint32_t buf_ctrl = bufctrl_prepare16(ep, ep->dpram_buf, is_rx) | USB_BUF_CTRL_SEL; // Note: device EP0 does not have an endpoint control register if (ep_reg != NULL) { uint32_t ep_ctrl = *ep_reg; + #if CFG_TUH_ENABLED + const bool force_single = (rp2usb_is_host_mode() && ep->interrupt_num > 0); + #else + const bool force_single = false; + #endif + if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data buf_ctrl |= (uint32_t)bufctrl_prepare16(ep, ep->dpram_buf + 64, is_rx) << 16; @@ -211,8 +217,7 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u (void)ff; hw_endpoint_lock_update(ep, 1); - if (ep->active) { - // TODO: Is this acceptable for interrupt packets? + if (ep->state == EPSTATE_ACTIVE) { TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr); rp2usb_reset_transfer(ep); } @@ -220,7 +225,7 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u // Fill in info now that we're kicking off the hw ep->remaining_len = total_len; ep->xferred_len = 0; - ep->active = true; + ep->state = EPSTATE_ACTIVE; #if CFG_TUD_EDPT_DEDICATED_HWFIFO if (ff != NULL) { @@ -229,66 +234,50 @@ void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, u } else #endif { - ep->user_buf = buffer; + ep->user_buf = buffer; + #if CFG_TUD_EDPT_DEDICATED_HWFIFO ep->is_xfer_fifo = false; + #endif } const bool is_host = rp2usb_is_host_mode(); + const bool is_rx = (is_host == (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN)); - if (ep->future_len > 0) { - // only on rx endpoint + #if CFG_TUD_ENABLED + if (!is_host && ep->future_len > 0) { + // Device only: previous short-packet abort saved data from the other buffer const uint8_t future_len = ep->future_len; memcpy(ep->user_buf, ep->dpram_buf + (ep->future_bufid << 6), future_len); ep->xferred_len += future_len; ep->remaining_len -= future_len; ep->user_buf += future_len; - ep->future_len = 0; ep->future_bufid = 0; if (ep->remaining_len == 0) { - // all data has been received, no need to start hw transfer - ep->active = false; const uint16_t xferred_len = ep->xferred_len; rp2usb_reset_transfer(ep); - - #if CFG_TUH_ENABLED - if (is_host) { - hcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); - } - #endif - #if CFG_TUD_ENABLED - if (!is_host) { - dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); - } - #endif - + dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, false); hw_endpoint_lock_update(ep, -1); return; } } - #if CFG_TUSB_RP2_ERRATA_E15 + #if CFG_TUSB_RP2_ERRATA_E15 if (ep->e15_bulk_in) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; // skip transfer if we are in critical frame period if (e15_is_critical_frame_period()) { - ep->pending = 1; + ep->state = EPSTATE_PENDING; hw_endpoint_lock_update(ep, -1); return; } } - #endif + #endif // CFG_TUSB_RP2_ERRATA_E15 + #endif // CFG_TUD_ENABLED - const bool is_rx = (is_host == (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN)); - #if CFG_TUH_ENABLED - const bool force_single = (is_host && ep->transfer_type == TUSB_XFER_INTERRUPT); - #else - const bool force_single = false; - #endif - - rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx, force_single); + rp2usb_buffer_start(ep, ep_reg, buf_reg, is_rx); hw_endpoint_lock_update(ep, -1); } @@ -333,7 +322,7 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ bool is_rx) { hw_endpoint_lock_update(ep, 1); - if (!ep->active) { + if (ep->state != EPSTATE_ACTIVE) { // probably land here due to short packet on rx with double buffered hw_endpoint_lock_update(ep, -1); return false; @@ -394,12 +383,12 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ if (buf_ctrl16_other & USB_BUF_CTRL_FULL) { // Data already sent into this buffer. Save it for the next transfer. // buff_status will be clear by the next run - if (is_host) { - // host put future_len pointer at end of epx_data - } else { + #if CFG_TUD_ENABLED + if (!is_host) { ep->future_len = (uint8_t)(buf_ctrl16_other & USB_BUF_CTRL_LEN_MASK); + ep->future_bufid = buf_id ^ 1; } - ep->future_bufid = buf_id ^ 1; + #endif } else { ep->next_pid ^= 1u; // roll back pid if aborted } @@ -422,10 +411,11 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ if (!is_done && ep->remaining_len > 0) { #if CFG_TUSB_RP2_ERRATA_E15 - if (ep->e15_bulk_in && e15_is_critical_frame_period()) { + const bool need_e15 = ep->e15_bulk_in; + if (need_e15 && e15_is_critical_frame_period()) { // mark as pending if matches E15 condition - ep->pending = 1; - } else if (ep->e15_bulk_in && ep->pending) { + ep->state = EPSTATE_PENDING; + } else if (need_e15 && ep->state == EPSTATE_PENDING) { // if already pending, meaning the other buf completes first, don't arm buffer, let SOF handle it // do nothing } else diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h index 2ba9d018e..8ebc3e3fc 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.h +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h @@ -32,9 +32,9 @@ // RP2040-E15: USB Device controller will hang if certain bus errors occur during an IN transfer. #ifndef CFG_TUSB_RP2_ERRATA_E15 #if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) - #define CFG_TUSB_RP2_ERRATA_E15 PICO_RP2040_USB_DEVICE_UFRAME_FIX + #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && PICO_RP2040_USB_DEVICE_UFRAME_FIX) #elif defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) - #define CFG_TUSB_RP2_ERRATA_E15 TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + #define CFG_TUSB_RP2_ERRATA_E15 (CFG_TUD_ENABLED && TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX) #endif #endif #endif @@ -90,18 +90,23 @@ enum { EPSTATE_IDLE = 0, EPSTATE_ACTIVE, EPSTATE_PENDING, + EPSTATE_PENDING_SETUP }; // Hardware information per endpoint typedef struct hw_endpoint { uint8_t ep_addr; uint8_t next_pid; - bool active; // transferring data - uint8_t pending; // Transfer scheduled but not active - bool is_xfer_fifo; // transfer using fifo + uint8_t state; - uint8_t future_bufid; - uint8_t future_len; +#if CFG_TUD_EDPT_DEDICATED_HWFIFO + bool is_xfer_fifo; // transfer using fifo +#endif + +#if CFG_TUD_ENABLED + uint8_t future_bufid; // which buffer holds next data + uint8_t future_len; // next data len +#endif #if CFG_TUSB_RP2_ERRATA_E15 bool e15_bulk_in; // Errata15 device bulk in @@ -110,8 +115,10 @@ typedef struct hw_endpoint { #if CFG_TUH_ENABLED uint8_t dev_addr; uint8_t interrupt_num; // 1-15 for interrupt endpoints - uint8_t transfer_type; - bool need_pre; // need preamble for low speed device behind full speed hub + struct TU_ATTR_PACKED { + uint8_t transfer_type : 2; + uint8_t need_pre : 1; // preamble for low-speed device behind full speed hub + }; #endif uint16_t max_packet_size; // max packet size also indicates configured @@ -153,7 +160,7 @@ TU_ATTR_ALWAYS_INLINE static inline void rp2usb_critical_exit(void) { void rp2usb_xfer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len); bool rp2usb_xfer_continue(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id, bool is_rx); -void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx, bool force_single); +void rp2usb_buffer_start(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, bool is_rx); void rp2usb_reset_transfer(hw_endpoint_t *ep); diff --git a/tools/metrics.py b/tools/metrics.py index 6b992c8f5..f624f382f 100644 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -166,6 +166,9 @@ def compute_avg(all_json_data): file_accumulator[fname]["symbols"][name].append(sym.get("size", 0)) sections_map = f.get("sections") or {} for sname, ssize in sections_map.items(): + # linkermap -v produces nested dicts {subsection: size}, flatten to total + if isinstance(ssize, dict): + ssize = sum(ssize.values()) file_accumulator[fname]["sections"][sname].append(ssize) # Build json_average with averaged values @@ -209,7 +212,7 @@ def compute_avg(all_json_data): def compare_files(base_file, new_file, filters=None): - """Compare two CSV or JSON inputs and generate difference report.""" + """Compare two CSV or JSON inputs and generate a difference report.""" filters = filters or [] base_avg = compute_avg(combine_files([base_file], filters)) -- cgit v1.3.1 From 087ceb7417e6c1d72701ae491407490cfe5c6558 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 1 Apr 2026 23:50:26 +0700 Subject: fix issue caused by merging active + pending state --- src/portable/raspberrypi/rp2040/dcd_rp2040.c | 2 +- src/portable/raspberrypi/rp2040/rp2040_usb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index ca03ebf8a..2b6bbc43b 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -253,7 +253,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { struct hw_endpoint *ep = hw_endpoint_get(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF - if (ep->e15_bulk_in && ep->state == EPSTATE_ACTIVE) { + if (ep->e15_bulk_in && ep->state >= EPSTATE_ACTIVE) { keep_sof_alive = true; hw_endpoint_lock_update(ep, 1); diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 206da041b..1b13934d3 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -322,7 +322,7 @@ bool __tusb_irq_path_func(rp2usb_xfer_continue)(hw_endpoint_t *ep, io_rw_32 *ep_ bool is_rx) { hw_endpoint_lock_update(ep, 1); - if (ep->state != EPSTATE_ACTIVE) { + if (ep->state == EPSTATE_IDLE) { // probably land here due to short packet on rx with double buffered hw_endpoint_lock_update(ep, -1); return false; -- cgit v1.3.1 From c0c1566bea34419a8b165a489278114363de5a5e Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 2 Apr 2026 23:50:12 +0700 Subject: rp2040: fix RP2350 hard fault in unaligned_memcpy to USB DPRAM Use volatile byte accesses to prevent the compiler from widening the byte-by-byte copy loop into 16/32-bit accesses, which cause a hard fault on RP2350 when targeting USB DPRAM (device memory). Closes #3554 --- src/portable/raspberrypi/rp2040/rp2040_usb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 1b13934d3..f5ba81dd7 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -51,10 +51,14 @@ critical_section_t rp2usb_lock; //--------------------------------------------------------------------+ // Implementation //--------------------------------------------------------------------+ -// Provide own byte by byte memcpy as not all copies are aligned +// Provide own byte by byte memcpy as not all copies are aligned. +// Use volatile to prevent compiler from widening to 16/32-bit accesses +// which cause hard fault on RP2350 when dst/src points to USB DPRAM. static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) { + volatile uint8_t *vdst = dst; + const volatile uint8_t *vsrc = src; while (n--) { - *dst++ = *src++; + *vdst++ = *vsrc++; } } -- cgit v1.3.1 From 9515ba8e795a582f4e62955b8d4398066e2acbb1 Mon Sep 17 00:00:00 2001 From: Ha Thach Date: Thu, 2 Apr 2026 23:55:12 +0700 Subject: Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/portable/raspberrypi/rp2040/rp2040_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index f5ba81dd7..83ac48ec2 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -53,7 +53,7 @@ critical_section_t rp2usb_lock; //--------------------------------------------------------------------+ // Provide own byte by byte memcpy as not all copies are aligned. // Use volatile to prevent compiler from widening to 16/32-bit accesses -// which cause hard fault on RP2350 when dst/src points to USB DPRAM. +// which cause hard fault on RP2350 when dst/src point to USB DPRAM. static void unaligned_memcpy(uint8_t *dst, const uint8_t *src, size_t n) { volatile uint8_t *vdst = dst; const volatile uint8_t *vsrc = src; -- cgit v1.3.1 From 72e09788e46a8ffaaae8009a316915838927af90 Mon Sep 17 00:00:00 2001 From: Boris Dolgov Date: Fri, 15 May 2026 21:28:21 +0200 Subject: rp2040: fix host SET_REPORT (and any OUT-data control xfer) sending DATA0 instead of DATA1 hcd_edpt_xfer() previously reset ep->next_pid to 1 only when the control endpoint direction changed between stages. That handled IN-data control transfers (e.g. GET_REPORT, GET_DESCRIPTOR) where SETUP is OUT and DATA is IN, but not OUT-data class requests like SET_REPORT, where SETUP and DATA are both OUT and the direction-change check is false. ep->next_pid was left at 0 from hcd_edpt_open(), so the DATA stage went on the wire as DATA0 when the device expected DATA1. Strict devices (observed: Elgato Stream Deck) treat this as a protocol violation and disconnect. Key off "endpoint 0" instead of "direction changed", restoring the previous behavior. Interrupt/bulk endpoints take the ep->interrupt_num > 0 branch above and never reach this code, so they are unaffected. --- src/portable/raspberrypi/rp2040/hcd_rp2040.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/portable/raspberrypi') diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 02a4e055e..064834efb 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -617,10 +617,16 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num); rp2usb_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, buflen); } else { - // Control endpoint can change direction 0x00 <-> 0x80 when changing stages - if (ep_addr != ep->ep_addr) { + // Control transfer data and status stages always start with DATA1, regardless of + // whether the direction changed since the previous stage. SET_REPORT (and any other + // host-to-device class request with an OUT data stage) keeps the same direction + // across SETUP -> DATA, so we cannot key off "direction changed" -- we must reset + // next_pid every time hcd_edpt_xfer is invoked on ep 0. Without this, the data stage + // of SET_REPORT goes out as DATA0 because ep->next_pid is still 0 from hcd_edpt_open(), + // which strict devices treat as a protocol violation and disconnect. + if (tu_edpt_number(ep_addr) == 0) { ep->ep_addr = ep_addr; - ep->next_pid = 1; // data and status stage start with DATA1 + ep->next_pid = 1; } // If EPX is busy with another transfer, mark as pending -- cgit v1.3.1