summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-18 17:59:30 +0700
committerhathach <[email protected]>2026-06-18 17:59:30 +0700
commitea5b8d677f22f4bd1574f864fd0487dfd329c090 (patch)
treec73641e9dee6992bdff0f12edf471427fed840a2
parent953abfb3931c67b834838c7fe0b31834cb0d42d6 (diff)
dcd/ch58x: drive CH582/583 with shared dcd_ch32_usbfs.c
Replace PR #3515's separate dcd_ch58x_usbfs.c / hcd_ch58x_usbfs.c with the shared WCH USBFS device driver (combined per-endpoint control, like CH32V103), adding two CH58x-specific behaviors guarded so CH32V103/V20x/V307 are unchanged: - CH32_USBFS_EP_MANUAL_TOG: CH58x's hardware AUTO_TOG does not stay in sync, so the ISR toggles DATA0/DATA1 manually and discards toggle-mismatched OUT packets. Fixes multi-packet bulk-IN (e.g. MSC READ10) that otherwise hung. - CH32_USBFS_EP4_SHARES_EP0: EP4 has no DMA register and overlays EP0's region (EP0[0:63] + EP4 OUT[64:127] + EP4 IN[128:191]); add a 192-byte shared buffer and buffer-pointer helpers (transparent for the other parts). Fixes cdc_dual_ports (Port1 is on EP4). Add the ch582m_evt board. Device only on USB0 (rhport 0): the shared hcd_ch32_usbfs.c is CH32V20x-specific and cannot drive CH58x, so host / USB2 (rhport 1) is left commented out in the BSP for easy re-add. Verified on ch582m_evt via local HIL: all device examples pass. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
-rw-r--r--hw/bsp/ch58x/boards/ch582m_evt/board.cmake5
-rw-r--r--hw/bsp/ch58x/boards/ch582m_evt/board.h61
-rw-r--r--hw/bsp/ch58x/boards/ch582m_evt/board.mk3
-rw-r--r--hw/bsp/ch58x/boards/yd-ch582m/board.h10
-rw-r--r--hw/bsp/ch58x/ch58x_it.h2
-rw-r--r--hw/bsp/ch58x/family.c27
-rw-r--r--hw/bsp/ch58x/family.cmake3
-rw-r--r--hw/bsp/ch58x/family.mk3
-rw-r--r--src/common/tusb_mcu.h11
-rw-r--r--src/portable/wch/ch32_usbfs_reg.h48
-rw-r--r--src/portable/wch/ch58x_usbfs_reg.h291
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c125
-rw-r--r--src/portable/wch/dcd_ch58x_usbfs.c636
-rw-r--r--src/portable/wch/hcd_ch58x_usbfs.c721
14 files changed, 244 insertions, 1702 deletions
diff --git a/hw/bsp/ch58x/boards/ch582m_evt/board.cmake b/hw/bsp/ch58x/boards/ch582m_evt/board.cmake
new file mode 100644
index 000000000..4129c4550
--- /dev/null
+++ b/hw/bsp/ch58x/boards/ch582m_evt/board.cmake
@@ -0,0 +1,5 @@
+set(LD_FLASH_SIZE 448K)
+set(LD_RAM_SIZE 32K)
+
+function(update_board TARGET)
+endfunction()
diff --git a/hw/bsp/ch58x/boards/ch582m_evt/board.h b/hw/bsp/ch58x/boards/ch582m_evt/board.h
new file mode 100644
index 000000000..c3483bf17
--- /dev/null
+++ b/hw/bsp/ch58x/boards/ch582m_evt/board.h
@@ -0,0 +1,61 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 TinyUSB contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* metadata:
+ name: CH582M-EVT evaluation board
+ url: https://www.wch-ic.com/products/CH582.html
+*/
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// LED: PB4 on CH582M-EVT
+#define LED_PIN GPIO_Pin_4
+#define LED_STATE_ON 0
+
+// Directly reuse BOOT pin as user button
+#define BUTTON_PIN GPIO_Pin_22
+#define BUTTON_STATE_ACTIVE 0
+
+// UART: UART1 TX=PA9, RX=PA8
+#define CFG_BOARD_UART_BAUDRATE 115200
+
+// Device only on USB1 (rhport 0). CH58x host / USB2 is not supported by the shared usbfs dcd;
+// BOARD_TUH_RHPORT is kept commented out to ease re-adding host later.
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT 0
+#endif
+// #ifndef BOARD_TUH_RHPORT
+// #define BOARD_TUH_RHPORT 1
+// #endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch58x/boards/ch582m_evt/board.mk b/hw/bsp/ch58x/boards/ch582m_evt/board.mk
new file mode 100644
index 000000000..a13979799
--- /dev/null
+++ b/hw/bsp/ch58x/boards/ch582m_evt/board.mk
@@ -0,0 +1,3 @@
+LDFLAGS += \
+ -Wl,--defsym=__FLASH_SIZE=448K \
+ -Wl,--defsym=__RAM_SIZE=32K \
diff --git a/hw/bsp/ch58x/boards/yd-ch582m/board.h b/hw/bsp/ch58x/boards/yd-ch582m/board.h
index a14ffd198..0da5747bc 100644
--- a/hw/bsp/ch58x/boards/yd-ch582m/board.h
+++ b/hw/bsp/ch58x/boards/yd-ch582m/board.h
@@ -45,14 +45,14 @@ extern "C" {
// UART: UART1 TX=PA9, RX=PA8
#define CFG_BOARD_UART_BAUDRATE 115200
-// Dual-port: USB1 (rhport 0) = Device, USB2 (rhport 1) = Host
-// Swap these two if you want the opposite assignment
+// Device only on USB1 (rhport 0). CH58x host / USB2 is not supported by the shared usbfs dcd;
+// BOARD_TUH_RHPORT is kept commented out to ease re-adding host later.
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
-#ifndef BOARD_TUH_RHPORT
-#define BOARD_TUH_RHPORT 1
-#endif
+// #ifndef BOARD_TUH_RHPORT
+// #define BOARD_TUH_RHPORT 1
+// #endif
#ifdef __cplusplus
}
diff --git a/hw/bsp/ch58x/ch58x_it.h b/hw/bsp/ch58x/ch58x_it.h
index 18ea52bc9..3e050344d 100644
--- a/hw/bsp/ch58x/ch58x_it.h
+++ b/hw/bsp/ch58x/ch58x_it.h
@@ -36,7 +36,7 @@ extern "C" {
void NMI_Handler(void);
void HardFault_Handler(void);
void USB_IRQHandler(void);
-void USB2_IRQHandler(void);
+// void USB2_IRQHandler(void); // host on USB2 (rhport 1) — re-add together with the host driver
void SysTick_Handler(void);
#ifdef __cplusplus
diff --git a/hw/bsp/ch58x/family.c b/hw/bsp/ch58x/family.c
index 3b1382e61..8e5f2826b 100644
--- a/hw/bsp/ch58x/family.c
+++ b/hw/bsp/ch58x/family.c
@@ -50,13 +50,16 @@
// Forward USB interrupt events to TinyUSB IRQ Handler
//--------------------------------------------------------------------+
+// Device only: the shared dcd_ch32_usbfs.c drives USB0 (rhport 0). CH58x's second controller
+// (USB2 / rhport 1) was driven by the now-removed ch58x host driver; its handler is kept
+// commented out below to ease re-adding host support.
__INTERRUPT __HIGH_CODE void USB_IRQHandler(void) {
tusb_int_handler(0, true);
}
-__INTERRUPT __HIGH_CODE void USB2_IRQHandler(void) {
- tusb_int_handler(1, true);
-}
+// __INTERRUPT __HIGH_CODE void USB2_IRQHandler(void) {
+// tusb_int_handler(1, true);
+// }
//--------------------------------------------------------------------+
// SysTick
@@ -111,16 +114,12 @@ void board_init(void) {
#endif
#endif
- // USB pin enable: enable analog function for USB1 and USB2 D+/D-
- R16_PIN_ANALOG_IE |= RB_PIN_USB_IE | RB_PIN_USB2_IE;
-
- // D+ pull-up is only needed for the device-role port
+ // Device only on USB0 (rhport 0): enable analog function for USB1 D+/D- and assert its D+
+ // pull-up. The shared dcd_ch32_usbfs.c drives USB0; CH58x host / USB2 (rhport 1) is unsupported
+ // here — to re-add host, also OR in RB_PIN_USB2_IE below.
+ R16_PIN_ANALOG_IE |= RB_PIN_USB_IE; // | RB_PIN_USB2_IE (re-add for host on USB2)
#if CFG_TUD_ENABLED
- #if BOARD_TUD_RHPORT == 0
- R16_PIN_ANALOG_IE |= RB_PIN_USB_DP_PU;
- #else
- R16_PIN_ANALOG_IE |= RB_PIN_USB2_DP_PU;
- #endif
+ R16_PIN_ANALOG_IE |= RB_PIN_USB_DP_PU;
#endif
// Keep USB clock active during sleep
@@ -164,6 +163,10 @@ uint32_t board_button_read(void) {
#endif
}
+// Note: CH58x exposes no memory-mapped unique-ID register (unlike ch32v10x/v20x at
+// 0x1FFFF7E8), and the SDK's GET_UNIQUE_ID() is a BootROM stub not present in
+// libISP583.a. So board_get_unique_id() falls back to the fixed default in board.c.
+
int board_uart_read(uint8_t* buf, int len) {
(void) buf;
(void) len;
diff --git a/hw/bsp/ch58x/family.cmake b/hw/bsp/ch58x/family.cmake
index b4835cb41..cbe1ffe72 100644
--- a/hw/bsp/ch58x/family.cmake
+++ b/hw/bsp/ch58x/family.cmake
@@ -77,8 +77,7 @@ function(family_configure_example TARGET RTOS)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/debug_uart.c
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
- ${TOP}/src/portable/wch/dcd_ch58x_usbfs.c
- ${TOP}/src/portable/wch/hcd_ch58x_usbfs.c
+ ${TOP}/src/portable/wch/dcd_ch32_usbfs.c
${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
)
target_include_directories(${TARGET} PUBLIC
diff --git a/hw/bsp/ch58x/family.mk b/hw/bsp/ch58x/family.mk
index 71c2cada6..2ef90a8c3 100644
--- a/hw/bsp/ch58x/family.mk
+++ b/hw/bsp/ch58x/family.mk
@@ -33,8 +33,7 @@ LDFLAGS_GCC += \
LIBS += $(TOP)/$(SDK_SRC_DIR)/StdPeriphDriver/libISP583.a
SRC_C += \
- src/portable/wch/dcd_ch58x_usbfs.c \
- src/portable/wch/hcd_ch58x_usbfs.c \
+ src/portable/wch/dcd_ch32_usbfs.c \
$(SDK_SRC_DIR)/StdPeriphDriver/CH58x_gpio.c \
$(SDK_SRC_DIR)/StdPeriphDriver/CH58x_clk.c \
$(SDK_SRC_DIR)/StdPeriphDriver/CH58x_uart1.c \
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 8408d3ae0..f2a2d92a5 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -660,17 +660,16 @@
#endif
#elif TU_CHECK_MCU(OPT_MCU_CH58X)
- // CH582/583 has 2 independent USBFS controllers with merged EP registers, FS only.
- #define TUP_USBIP_WCH_CH58X
+ // CH582/583 USBFS: older WCH USBFS IP with a single combined per-endpoint control register
+ // (like CH32V103), driven by the shared dcd_ch32_usbfs.c on USB0 (rhport 0). Device only:
+ // the shared hcd_ch32_usbfs.c is CH32V20x-specific and does not support CH58x, so host /
+ // USB2 (rhport 1) is not provided here.
+ #define TUP_USBIP_WCH_USBFS
#ifndef CFG_TUD_WCH_USBIP_USBFS
#define CFG_TUD_WCH_USBIP_USBFS 1
#endif
- #ifndef CFG_TUH_WCH_USBIP_USBFS
- #define CFG_TUH_WCH_USBIP_USBFS 1
- #endif
-
#define TUP_DCD_ENDPOINT_MAX 8
//--------------------------------------------------------------------+
diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h
index 7ffdc6cef..0d61e183c 100644
--- a/src/portable/wch/ch32_usbfs_reg.h
+++ b/src/portable/wch/ch32_usbfs_reg.h
@@ -130,6 +130,53 @@
#elif CFG_TUSB_MCU == OPT_MCU_CH32V307
#include <ch32v30x.h>
#define USBHD_IRQn OTG_FS_IRQn
+#elif CFG_TUSB_MCU == OPT_MCU_CH58X
+ #include "CH58x_common.h"
+ // CH582/583 USBFS device controller: same combined per-endpoint control register as
+ // CH32V103 (IN response bits[1:0], OUT response bits[3:2]) but a different register map -
+ // the EP control/length block sits lower (EP0_CTRL @ +0x22), EP5-7 are split out, EP4
+ // shares EP0's DMA buffer, and EP5/6/7 mode bits live in one UEP567_MOD. The control/status
+ // block matches CH32. Two FS controllers exist (USB @ 0x40008000, USB2 @ 0x40008400); the
+ // device uses USB0. EP registers are accessed via the CH58X macros below (not the struct).
+ #define CH58X_USBFS_BASE 0x40008000u
+ typedef struct {
+ __IO uint8_t BASE_CTRL; // 0x00
+ __IO uint8_t UDEV_CTRL; // 0x01
+ __IO uint8_t INT_EN; // 0x02
+ __IO uint8_t DEV_ADDR; // 0x03
+ __IO uint8_t Reserve0; // 0x04
+ __IO uint8_t MIS_ST; // 0x05
+ __IO uint8_t INT_FG; // 0x06
+ __IO uint8_t INT_ST; // 0x07
+ __IO uint8_t RX_LEN; // 0x08 (8-bit on CH58X)
+ __IO uint8_t Reserve1[3]; // 0x09..0x0B
+ __IO uint8_t UEP4_1_MOD; // 0x0C
+ __IO uint8_t UEP2_3_MOD; // 0x0D
+ __IO uint8_t UEP567_MOD; // 0x0E
+ } USBOTG_FS_TypeDef;
+ #define USBOTG_FS ((USBOTG_FS_TypeDef *) CH58X_USBFS_BASE)
+
+ #define CH32_USBFS_EP_CTRL_COMBINED 1
+ #define CH32_USBFS_EP_REGS_CUSTOM 1 // EP register macros provided here, not by the driver
+ // CH58x's hardware AUTO_TOG does not stay in sync (notably across clear-stall and multi-packet
+ // bulk transfers), causing data-toggle mismatch and bus resets. Drive the toggle manually in
+ // the ISR instead. CH32V103/V20x/V307 keep AUTO_TOG (this macro is undefined for them).
+ #define CH32_USBFS_EP_MANUAL_TOG 1
+ // CH58x EP4 has no DMA register of its own: it overlays EP0's DMA region as
+ // EP0[0:63] + EP4_OUT[64:127] + EP4_IN[128:191], so EP0 needs a 192-byte buffer.
+ #define CH32_USBFS_EP4_SHARES_EP0 1
+ #define USBHD_IRQn USB_IRQn
+ #ifndef NVIC_EnableIRQ
+ #define NVIC_EnableIRQ(n) PFIC_EnableIRQ(n)
+ #define NVIC_DisableIRQ(n) PFIC_DisableIRQ(n)
+ #endif
+
+ // EP register access. EP0-4: T_LEN @ +0x20+ep*4, CTRL @ +0x22+ep*4. EP5-7 split: T_LEN @
+ // +0x64, CTRL @ +0x66. DMA: EP0-3 @ +0x10+ep*4, EP5-7 @ +0x54; EP4 shares EP0's buffer
+ // (no own DMA reg) so its slot points at a reserved word.
+ #define EP_TX_LEN(ep) (*(volatile uint8_t *)(CH58X_USBFS_BASE + ((ep) <= 4u ? 0x20u + (ep)*4u : 0x64u + ((ep)-5u)*4u)))
+ #define EP_CTRL(ep) (*(volatile uint8_t *)(CH58X_USBFS_BASE + ((ep) <= 4u ? 0x22u + (ep)*4u : 0x66u + ((ep)-5u)*4u)))
+ #define EP_DMA(ep) (*(volatile uint16_t *)(CH58X_USBFS_BASE + ((ep) <= 3u ? 0x10u + (ep)*4u : (ep) == 4u ? 0x40u : 0x54u + ((ep)-5u)*4u)))
#endif
#ifdef __GNUC__
@@ -170,6 +217,7 @@
// INT_ST
#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F)
#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03)
+#define USBFS_INT_ST_TOG_OK (1 << 6) // received packet's data toggle matched expectation
// UDEV_CTRL
#define USBFS_UDEV_CTRL_PORT_EN (1 << 0)
diff --git a/src/portable/wch/ch58x_usbfs_reg.h b/src/portable/wch/ch58x_usbfs_reg.h
deleted file mode 100644
index 67b47e0b6..000000000
--- a/src/portable/wch/ch58x_usbfs_reg.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2024 TinyUSB contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef CH58X_USBFS_REG_H
-#define CH58X_USBFS_REG_H
-
-#include <stdint.h>
-
-//--------------------------------------------------------------------+
-// USB Base Addresses
-//--------------------------------------------------------------------+
-#define CH58X_USB_BASE 0x40008000u
-#define CH58X_USB2_BASE 0x40008400u
-
-//--------------------------------------------------------------------+
-// Global Control / Status Registers
-//--------------------------------------------------------------------+
-#define CH58X_USB_CTRL(base) (*(volatile uint8_t *)((base) + 0x00))
-#define CH58X_UDEV_CTRL(base) (*(volatile uint8_t *)((base) + 0x01))
-#define CH58X_USB_INT_EN(base) (*(volatile uint8_t *)((base) + 0x02))
-#define CH58X_USB_DEV_AD(base) (*(volatile uint8_t *)((base) + 0x03))
-#define CH58X_USB_MIS_ST(base) (*(volatile uint8_t *)((base) + 0x05))
-#define CH58X_USB_INT_FG(base) (*(volatile uint8_t *)((base) + 0x06))
-#define CH58X_USB_INT_ST(base) (*(volatile uint8_t *)((base) + 0x07))
-#define CH58X_USB_RX_LEN(base) (*(volatile uint8_t *)((base) + 0x08))
-
-//--------------------------------------------------------------------+
-// Endpoint Mode Registers
-//--------------------------------------------------------------------+
-#define CH58X_UEP4_1_MOD(base) (*(volatile uint8_t *)((base) + 0x0C))
-#define CH58X_UEP2_3_MOD(base) (*(volatile uint8_t *)((base) + 0x0D))
-#define CH58X_UEP567_MOD(base) (*(volatile uint8_t *)((base) + 0x0E))
-
-//--------------------------------------------------------------------+
-// Endpoint DMA / T_LEN / CTRL register accessors
-//--------------------------------------------------------------------+
-
-// EP DMA is 16-bit (only low 16 bits of RAM address, high bits implied 0x2000), EP4 shares EP0's DMA register (no independent DMA), so ep==4 maps to base+0x10
-#define CH58X_EP_DMA(base, ep) (*(volatile uint16_t *)((base) + ((ep) <= 3 ? (0x10u + (ep)*4u) : ((ep) == 4 ? 0x10u : (0x54u + ((ep)-5u)*4u)))))
-#define CH58X_EP_TLEN(base, ep) (*(volatile uint8_t *)((base) + ((ep) <= 4 ? (0x20u + (ep)*4u) : (0x64u + ((ep)-5u)*4u))))
-#define CH58X_EP_CTRL(base, ep) (*(volatile uint8_t *)((base) + ((ep) <= 4 ? (0x22u + (ep)*4u) : (0x66u + ((ep)-5u)*4u))))
-
-//--------------------------------------------------------------------+
-// USB_CTRL (R8_USB_CTRL) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UC_DMA_EN 0x01
-#define CH58X_UC_CLR_ALL 0x02
-#define CH58X_UC_RESET_SIE 0x04
-#define CH58X_UC_INT_BUSY 0x08
-#define CH58X_UC_SYS_CTRL 0x10
-#define CH58X_UC_DEV_PU_EN 0x20
-#define CH58X_UC_LOW_SPEED 0x40
-#define CH58X_UC_HOST_MODE 0x80
-
-//--------------------------------------------------------------------+
-// UDEV_CTRL (R8_UDEV_CTRL) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UD_PORT_EN 0x01
-#define CH58X_UD_GP_BIT 0x02
-#define CH58X_UD_LOW_SPEED 0x04
-#define CH58X_UD_PD_DIS 0x80
-
-//--------------------------------------------------------------------+
-// INT_EN (R8_USB_INT_EN) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UIE_BUS_RST 0x01
-#define CH58X_UIE_DETECT 0x01 /* host mode alias */
-#define CH58X_UIE_TRANSFER 0x02
-#define CH58X_UIE_SUSPEND 0x04
-#define CH58X_UIE_HST_SOF 0x08
-#define CH58X_UIE_FIFO_OV 0x10
-
-//--------------------------------------------------------------------+
-// INT_FG (R8_USB_INT_FG) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UIF_BUS_RST 0x01
-#define CH58X_UIF_DETECT 0x01 /* host mode alias */
-#define CH58X_UIF_TRANSFER 0x02
-#define CH58X_UIF_SUSPEND 0x04
-#define CH58X_UIF_HST_SOF 0x08
-#define CH58X_UIF_FIFO_OV 0x10
-#define CH58X_U_SIE_FREE 0x20
-#define CH58X_U_TOG_OK 0x40
-#define CH58X_U_IS_NAK 0x80
-
-//--------------------------------------------------------------------+
-// INT_ST (R8_USB_INT_ST) parsing
-//--------------------------------------------------------------------+
-#define CH58X_INT_ST_ENDP(x) (((x) >> 0) & 0x0F)
-#define CH58X_INT_ST_TOKEN(x) (((x) >> 4) & 0x03)
-#define CH58X_UIS_TOG_OK 0x40 // toggle match flag (device and host)
-#define CH58X_UIS_SETUP_ACT 0x80
-
-// Token PID values
-#define CH58X_PID_OUT 0
-#define CH58X_PID_SOF 1
-#define CH58X_PID_IN 2
-#define CH58X_PID_SETUP 3
-
-//--------------------------------------------------------------------+
-// MIS_ST (R8_USB_MIS_ST) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UMS_DEV_ATTACH 0x01
-#define CH58X_UMS_DM_LEVEL 0x02
-#define CH58X_UMS_SUSPEND 0x04
-#define CH58X_UMS_BUS_RESET 0x08
-#define CH58X_UMS_R_FIFO_RDY 0x10
-#define CH58X_UMS_SIE_FREE 0x20
-#define CH58X_UMS_SOF_ACT 0x40
-#define CH58X_UMS_SOF_PRES 0x80
-
-//--------------------------------------------------------------------+
-// EP CTRL register bit definitions (merged TX+RX in single 8-bit reg)
-//
-// Bit 7: RB_UEP_R_TOG RX data toggle
-// Bit 6: RB_UEP_T_TOG TX data toggle
-// Bit 5: (reserved)
-// Bit 4: RB_UEP_AUTO_TOG auto toggle (EP1/2/3/5/6/7, not EP0/EP4)
-// Bit 3: R_RES1 RX response high
-// Bit 2: R_RES0 RX response low
-// Bit 1: T_RES1 TX response high
-// Bit 0: T_RES0 TX response low
-//--------------------------------------------------------------------+
-
-// TX response bits[1:0]
-#define CH58X_EP_T_RES_MASK 0x03
-#define CH58X_EP_T_RES_ACK 0x00
-#define CH58X_EP_T_RES_TOUT 0x01 /* ISO: no handshake */
-#define CH58X_EP_T_RES_NAK 0x02
-#define CH58X_EP_T_RES_STALL 0x03
-
-// RX response bits[3:2]
-#define CH58X_EP_R_RES_MASK 0x0C
-#define CH58X_EP_R_RES_ACK 0x00
-#define CH58X_EP_R_RES_TOUT 0x04 /* ISO: no handshake */
-#define CH58X_EP_R_RES_NAK 0x08
-#define CH58X_EP_R_RES_STALL 0x0C
-
-// Toggle and auto-toggle
-#define CH58X_EP_AUTO_TOG 0x10 /* bit 4, shared TX/RX */
-#define CH58X_EP_T_TOG 0x40 /* bit 6, TX DATA toggle */
-#define CH58X_EP_R_TOG 0x80 /* bit 7, RX DATA toggle */
-
-//--------------------------------------------------------------------+
-// EP Mode register (UEP4_1_MOD) bit definitions
-//--------------------------------------------------------------------+
-// R8_UEP4_1_MOD: EP4 in bits[3:2], EP1 in bits[7:4]
-#define CH58X_UEP1_BUF_MOD 0x10
-#define CH58X_UEP1_TX_EN 0x40
-#define CH58X_UEP1_RX_EN 0x80
-#define CH58X_UEP4_TX_EN 0x04
-#define CH58X_UEP4_RX_EN 0x08
-
-// R8_UEP2_3_MOD: EP3 in bits[7:4], EP2 in bits[3:0]
-#define CH58X_UEP2_BUF_MOD 0x01
-#define CH58X_UEP2_TX_EN 0x04
-#define CH58X_UEP2_RX_EN 0x08
-#define CH58X_UEP3_BUF_MOD 0x10
-#define CH58X_UEP3_TX_EN 0x40
-#define CH58X_UEP3_RX_EN 0x80
-
-// R8_UEP567_MOD
-#define CH58X_UEP5_TX_EN 0x01
-#define CH58X_UEP5_RX_EN 0x02
-#define CH58X_UEP6_TX_EN 0x04
-#define CH58X_UEP6_RX_EN 0x08
-#define CH58X_UEP7_TX_EN 0x10
-#define CH58X_UEP7_RX_EN 0x20
-
-//--------------------------------------------------------------------+
-// Host-mode Register Aliases
-// In host mode: EP2 -> RX, EP3 -> TX, EP1_CTRL -> SETUP
-//--------------------------------------------------------------------+
-#define CH58X_UHOST_CTRL(base) (*(volatile uint8_t *)((base) + 0x01)) // = UDEV_CTRL
-#define CH58X_UH_EP_MOD(base) (*(volatile uint8_t *)((base) + 0x0D)) // = UEP2_3_MOD
-#define CH58X_UH_RX_DMA(base) (*(volatile uint16_t *)((base) + 0x18)) // = UEP2_DMA
-#define CH58X_UH_TX_DMA(base) (*(volatile uint16_t *)((base) + 0x1C)) // = UEP3_DMA
-#define CH58X_UH_SETUP(base) (*(volatile uint8_t *)((base) + 0x26)) // = UEP1_CTRL
-#define CH58X_UH_EP_PID(base) (*(volatile uint8_t *)((base) + 0x28)) // = UEP2_T_LEN
-#define CH58X_UH_RX_CTRL(base) (*(volatile uint8_t *)((base) + 0x2A)) // = UEP2_CTRL
-#define CH58X_UH_TX_LEN(base) (*(volatile uint8_t *)((base) + 0x2C)) // = UEP3_T_LEN
-#define CH58X_UH_TX_CTRL(base) (*(volatile uint8_t *)((base) + 0x2E)) // = UEP3_CTRL
-
-//--------------------------------------------------------------------+
-// UHOST_CTRL (R8_UHOST_CTRL) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UH_PD_DIS 0x80
-#define CH58X_UH_LOW_SPEED 0x04
-#define CH58X_UH_BUS_RESET 0x02
-#define CH58X_UH_PORT_EN 0x01
-
-//--------------------------------------------------------------------+
-// UH_EP_MOD (R8_UH_EP_MOD) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UH_EP_TX_EN 0x40
-#define CH58X_UH_EP_TBUF_MOD 0x10
-#define CH58X_UH_EP_RX_EN 0x08
-#define CH58X_UH_EP_RBUF_MOD 0x01
-
-//--------------------------------------------------------------------+
-// UH_SETUP (R8_UH_SETUP) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UH_PRE_PID_EN 0x80
-#define CH58X_UH_SOF_EN 0x40
-
-//--------------------------------------------------------------------+
-// UH_RX_CTRL (R8_UH_RX_CTRL) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UH_R_TOG 0x80
-#define CH58X_UH_R_AUTO_TOG 0x10
-#define CH58X_UH_R_RES 0x04
-
-//--------------------------------------------------------------------+
-// UH_TX_CTRL (R8_UH_TX_CTRL) bit definitions
-//--------------------------------------------------------------------+
-#define CH58X_UH_T_TOG 0x40
-#define CH58X_UH_T_AUTO_TOG 0x10
-#define CH58X_UH_T_RES 0x01
-
-//--------------------------------------------------------------------+
-// USB_INT_ST host-mode bits
-//--------------------------------------------------------------------+
-#define CH58X_UIS_H_RES_MASK 0x0F
-
-//--------------------------------------------------------------------+
-// USB_DEV_AD bits
-//--------------------------------------------------------------------+
-#define CH58X_UDA_GP_BIT 0x80
-#define CH58X_USB_ADDR_MASK 0x7F
-
-//--------------------------------------------------------------------+
-// Standard USB PID values (for host token and response)
-//--------------------------------------------------------------------+
-#define CH58X_USB_PID_OUT 0x01
-#define CH58X_USB_PID_IN 0x09
-#define CH58X_USB_PID_SOF 0x05
-#define CH58X_USB_PID_SETUP 0x0D
-#define CH58X_USB_PID_DATA0 0x03
-#define CH58X_USB_PID_DATA1 0x0B
-#define CH58X_USB_PID_ACK 0x02
-#define CH58X_USB_PID_NAK 0x0A
-#define CH58X_USB_PID_STALL 0x0E
-
-//--------------------------------------------------------------------+
-// PIN_ANALOG_IE register
-//--------------------------------------------------------------------+
-#define CH58X_PIN_ANALOG_IE (*(volatile uint16_t *)0x4000101A)
-#define CH58X_PIN_USB_DP_PU 0x40
-#define CH58X_PIN_USB_IE 0x80
-#define CH58X_PIN_USB2_DP_PU 0x10
-#define CH58X_PIN_USB2_IE 0x20
-
-//--------------------------------------------------------------------+
-// Sleep clock control
-//--------------------------------------------------------------------+
-#define CH58X_SLP_CLK_OFF1 (*(volatile uint8_t *)0x4000100D)
-#define CH58X_SLP_CLK_USB 0x10
-
-//--------------------------------------------------------------------+
-// PFIC (Platform-level Fast Interrupt Controller)
-//--------------------------------------------------------------------+
-#define CH58X_PFIC_IENR ((volatile uint32_t *)0xE000E100) // interrupt enable
-#define CH58X_PFIC_IRER ((volatile uint32_t *)0xE000E180) // interrupt reset (disable)
-
-#define CH58X_USB_IRQn 22
-#define CH58X_USB2_IRQn 23
-
-#endif /* CH58X_USBFS_REG_H */
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
index dae31da91..7beb91e16 100644
--- a/src/portable/wch/dcd_ch32_usbfs.c
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -35,17 +35,23 @@
/* private defines */
#define EP_MAX (8)
+ // Struct-based EP register access (uniform layout). Some parts (e.g. CH58X) have a different
+ // register map and define EP_DMA/EP_TX_LEN/EP_CTRL themselves in ch32_usbfs_reg.h.
+ #ifndef CH32_USBFS_EP_REGS_CUSTOM
#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep])
#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
+ #endif
// Endpoint control register access. The newer USBFS IP (CH32V20x/V307/X035) has separate
// TX_CTRL and RX_CTRL bytes per endpoint; the older IP (CH32V103) has a single combined
// UEPn_CTRL register. These helpers hide the difference so the rest of the driver is shared.
// Values use the newer-IP encoding (USBFS_EP_T_*/USBFS_EP_R_*); the combined path remaps them.
#ifdef CH32_USBFS_EP_CTRL_COMBINED
+ #ifndef EP_CTRL // parts with a custom register map (CH58X) define EP_CTRL directly in reg.h
#define EP_CTRL(ep) EP_TX_CTRL(ep) // UEPn_TX_CTRL field aliases the combined UEPn_CTRL register
+ #endif
static inline uint8_t ep_tx_to_comb(uint8_t v) {
uint8_t c = v & USBFS_EP_T_RES_MASK; // IN response: bits [1:0] in both encodings
@@ -86,6 +92,17 @@
#define EP0_SETUP_RX_TOG 0
#endif
+// Hardware auto data-toggle flag. Parts whose AUTO_TOG is reliable OR it into the EP setup so the
+// controller flips DATA0/DATA1 itself; CH58x (CH32_USBFS_EP_MANUAL_TOG) leaves it clear and the
+// ISR flips the toggle bit after each packet instead.
+#ifdef CH32_USBFS_EP_MANUAL_TOG
+ #define EP_T_AUTO_TOG 0
+ #define EP_R_AUTO_TOG 0
+#else
+ #define EP_T_AUTO_TOG USBFS_EP_T_AUTO_TOG
+ #define EP_R_AUTO_TOG USBFS_EP_R_AUTO_TOG
+#endif
+
/* private data */
struct usb_xfer {
bool valid;
@@ -100,6 +117,12 @@ static struct {
bool isochronous[EP_MAX];
struct usb_xfer xfer[EP_MAX][2];
TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
+#ifdef CH32_USBFS_EP4_SHARES_EP0
+ // CH58X: EP0 and EP4 share one DMA region (EP4 has no DMA register). Layout:
+ // EP0 [0:63] (half-duplex, OUT+IN) + EP4 OUT [64:127] + EP4 IN [128:191]. buffer[0]/buffer[4]
+ // are left unused for this part.
+ TU_ATTR_ALIGNED(4) uint8_t ep0_ep4_buffer[3 * 64];
+#endif
TU_ATTR_ALIGNED(4) struct {
// OUT transfers >64 bytes will overwrite queued IN data!
uint8_t out[64];
@@ -108,19 +131,50 @@ static struct {
} ep3_buffer;
} data;
+// DMA / copy buffer pointers per endpoint. The WCH USBFS buffer holds OUT (RX) at offset 0 and
+// IN (TX) at +64; EP0 is half-duplex and reuses its OUT chunk for IN; EP3 has an enlarged IN
+// buffer for throughput. On CH58X, EP4 overlays EP0's region (see ep0_ep4_buffer above).
+static inline uint32_t ep_dma_addr(uint8_t ep) {
+#ifdef CH32_USBFS_EP4_SHARES_EP0
+ if (ep == 0) { return (uint32_t) &data.ep0_ep4_buffer[0]; }
+#endif
+ if (ep == 3) { return (uint32_t) &data.ep3_buffer.out[0]; }
+ return (uint32_t) &data.buffer[ep][0];
+}
+static inline uint8_t* ep_out_buf(uint8_t ep) {
+#ifdef CH32_USBFS_EP4_SHARES_EP0
+ if (ep == 0) { return &data.ep0_ep4_buffer[0]; }
+ if (ep == 4) { return &data.ep0_ep4_buffer[64]; }
+#endif
+ if (ep == 3) { return data.ep3_buffer.out; }
+ return data.buffer[ep][TUSB_DIR_OUT];
+}
+static inline uint8_t* ep_in_buf(uint8_t ep) {
+#ifdef CH32_USBFS_EP4_SHARES_EP0
+ if (ep == 0) { return &data.ep0_ep4_buffer[0]; } // EP0 half-duplex: IN reuses OUT chunk
+ if (ep == 4) { return &data.ep0_ep4_buffer[128]; }
+#endif
+ if (ep == 0) { return data.buffer[0][TUSB_DIR_OUT]; } // EP0 half-duplex: IN reuses OUT chunk
+ if (ep == 3) { return data.ep3_buffer.in; }
+ return data.buffer[ep][TUSB_DIR_IN];
+}
+// EP4 on CH58X has no DMA register (shares EP0's); skip its EP_DMA() write.
+static inline bool ep_shares_ep0_dma(uint8_t ep) {
+#ifdef CH32_USBFS_EP4_SHARES_EP0
+ return ep == 4;
+#else
+ (void) ep;
+ return false;
+#endif
+}
+
/* private helpers */
static void update_in(uint8_t rhport, uint8_t ep, bool force) {
struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_IN];
if (xfer->valid) {
if (force || xfer->len) {
size_t len = TU_MIN(xfer->max_size, xfer->len);
- if (ep == 0) {
- memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
- } else if (ep == 3) {
- memcpy(data.ep3_buffer.in, xfer->buffer, len);
- } else {
- memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
- }
+ memcpy(ep_in_buf(ep), xfer->buffer, len);
xfer->buffer += len;
xfer->len -= len;
xfer->processed_len += len;
@@ -150,11 +204,7 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_OUT];
if (xfer->valid) {
size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
- if (ep == 3) {
- memcpy(xfer->buffer, data.ep3_buffer.out, len);
- } else {
- memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
- }
+ memcpy(xfer->buffer, ep_out_buf(ep), len);
xfer->buffer += len;
xfer->len -= len;
xfer->processed_len += len;
@@ -176,12 +226,11 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
static void reset_ep_ctrls(void) {
for (uint8_t ep = 1; ep < EP_MAX; ep++) {
- EP_DMA(ep) = (uint32_t)&data.buffer[ep][0];
+ if (!ep_shares_ep0_dma(ep)) { EP_DMA(ep) = ep_dma_addr(ep); }
EP_TX_LEN(ep) = 0;
- ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NYET);
- ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET);
+ ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NYET);
+ ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET);
}
- EP_DMA(3) = (uint32_t)&data.ep3_buffer.out[0];
}
/* public functions */
@@ -195,8 +244,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
USBOTG_FS->INT_FG = 0xFF;
USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
- // setup endpoint 0
- EP_DMA(0) = (uint32_t)&data.buffer[0][0];
+ // setup endpoint 0 (also backs EP4's buffer on CH58X via the shared DMA region)
+ EP_DMA(0) = ep_dma_addr(0);
EP_TX_LEN(0) = 0;
ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK);
ep_rx_ctrl_set(0, USBFS_EP_R_RES_ACK);
@@ -204,8 +253,14 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
// enable other endpoints but NAK everything
USBOTG_FS->UEP4_1_MOD = 0xCC;
USBOTG_FS->UEP2_3_MOD = 0xCC;
+#ifdef CH32_USBFS_EP_REGS_CUSTOM
+ // CH58X: a single mode register enables EP5/6/7 RX+TX (different bit layout than CH32).
+ USBOTG_FS->UEP567_MOD = RB_UEP5_RX_EN | RB_UEP5_TX_EN | RB_UEP6_RX_EN | RB_UEP6_TX_EN |
+ RB_UEP7_RX_EN | RB_UEP7_TX_EN;
+#else
USBOTG_FS->UEP5_6_MOD = 0xCC;
USBOTG_FS->UEP7_MOD = 0x0C;
+#endif
reset_ep_ctrls();
@@ -218,17 +273,31 @@ void dcd_int_handler(uint8_t rhport) {
(void)rhport;
uint8_t status = USBOTG_FS->INT_FG;
if (status & USBFS_INT_FG_TRANSFER) {
- uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
- uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
+ uint8_t int_st = USBOTG_FS->INT_ST;
+ uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(int_st);
+ uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(int_st);
uint16_t rx_len = USBOTG_FS->RX_LEN;
switch (token) {
case PID_OUT: {
+#ifdef CH32_USBFS_EP_MANUAL_TOG
+ // Manual toggle: drop OUT packets whose data toggle doesn't match (host retransmit),
+ // otherwise flip the expected RX toggle for the next packet. EP0 is driven by the
+ // SETUP/status flow below, so its toggle is left to that path.
+ if (ep != 0) {
+ if (!(int_st & USBFS_INT_ST_TOG_OK)) { break; }
+ EP_CTRL(ep) ^= USBFS_EPC_R_TOG;
+ }
+#endif
update_out(rhport, ep, rx_len);
break;
}
case PID_IN:
+#ifdef CH32_USBFS_EP_MANUAL_TOG
+ // Manual toggle: flip the TX toggle after each ACK'd IN packet (EP0 manages its own).
+ if (ep != 0) { EP_CTRL(ep) ^= USBFS_EPC_T_TOG; }
+#endif
update_in(rhport, ep, false);
break;
@@ -237,11 +306,12 @@ void dcd_int_handler(uint8_t rhport) {
ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK);
data.ep0_tog = true;
- const tusb_control_request_t *setup = (const tusb_control_request_t *)&data.buffer[0][TUSB_DIR_OUT][0];
+ uint8_t *ep0_out = ep_out_buf(0);
+ const tusb_control_request_t *setup = (const tusb_control_request_t *)ep0_out;
// EP0_SETUP_RX_TOG arms the data/status stage at DATA1 on the combined-control IP
ep_rx_ctrl_set(0, ((setup->wLength == 0) ? USBFS_EP_R_RES_ACK : USBFS_EP_R_RES_NAK) | EP0_SETUP_RX_TOG);
- dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
+ dcd_event_setup_received(rhport, ep0_out, true);
break;
}
@@ -323,10 +393,12 @@ bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) {
data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
if (ep != 0) {
+ // Opening clears the toggle to DATA0 (ep_*_ctrl_set writes the toggle bit clear since v has no
+ // R/T_TOG); with manual toggle EP_*_AUTO_TOG is 0 so the ISR owns subsequent toggling.
if (dir == TUSB_DIR_OUT) {
- ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK);
+ ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK);
} else {
- ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK);
+ ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK);
}
}
return true;
@@ -407,10 +479,11 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
ep_rx_ctrl_set(0, USBFS_EP_R_RES_ACK);
}
} else {
+ // clear-stall resets the toggle to DATA0 (USB spec); manual-toggle parts then re-sync via ISR
if (dir == TUSB_DIR_OUT) {
- ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK);
+ ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK);
} else {
- ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK);
+ ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK);
}
}
}
diff --git a/src/portable/wch/dcd_ch58x_usbfs.c b/src/portable/wch/dcd_ch58x_usbfs.c
deleted file mode 100644
index c6f10b801..000000000
--- a/src/portable/wch/dcd_ch58x_usbfs.c
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2024 TinyUSB contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_CH58X) && CFG_TUD_WCH_USBIP_USBFS
-
-#include "device/dcd.h"
-#include "ch58x_usbfs_reg.h"
-
-//--------------------------------------------------------------------+
-// Configuration
-//--------------------------------------------------------------------+
-#define EP_MAX 8
-#define EP_BUF_SIZE 64
-
-//--------------------------------------------------------------------+
-// USB base address selection by rhport
-//--------------------------------------------------------------------+
-static inline uint32_t get_usb_base(uint8_t rhport) {
- return (rhport == 0) ? CH58X_USB_BASE : CH58X_USB2_BASE;
-}
-
-//--------------------------------------------------------------------+
-// Register access helpers using base address
-//--------------------------------------------------------------------+
-#define USB_CTRL(base) CH58X_USB_CTRL(base)
-#define USB_UDEV_CTRL(base) CH58X_UDEV_CTRL(base)
-#define USB_INT_EN(base) CH58X_USB_INT_EN(base)
-#define USB_DEV_AD(base) CH58X_USB_DEV_AD(base)
-#define USB_MIS_ST(base) CH58X_USB_MIS_ST(base)
-#define USB_INT_FG(base) CH58X_USB_INT_FG(base)
-#define USB_INT_ST(base) CH58X_USB_INT_ST(base)
-#define USB_RX_LEN(base) CH58X_USB_RX_LEN(base)
-
-#define EP_DMA(base, ep) CH58X_EP_DMA(base, ep)
-#define EP_TLEN(base, ep) CH58X_EP_TLEN(base, ep)
-#define EP_CTRL(base, ep) CH58X_EP_CTRL(base, ep)
-
-//--------------------------------------------------------------------+
-// Inline helpers for merged EP CTRL register
-//--------------------------------------------------------------------+
-static inline void ep_set_tx_response(uint32_t base, uint8_t ep, uint8_t resp) {
- uint8_t ctrl = EP_CTRL(base, ep);
- ctrl = (ctrl & ~CH58X_EP_T_RES_MASK) | resp;
- EP_CTRL(base, ep) = ctrl;
-}
-
-static inline void ep_set_rx_response(uint32_t base, uint8_t ep, uint8_t resp) {
- uint8_t ctrl = EP_CTRL(base, ep);
- ctrl = (ctrl & ~CH58X_EP_R_RES_MASK) | resp;
- EP_CTRL(base, ep) = ctrl;
-}
-
-static inline void ep_set_both_response(uint32_t base, uint8_t ep, uint8_t tx_resp, uint8_t rx_resp) {
- uint8_t ctrl = EP_CTRL(base, ep);
- ctrl = (ctrl & ~(CH58X_EP_T_RES_MASK | CH58X_EP_R_RES_MASK)) | tx_resp | rx_resp;
- EP_CTRL(base, ep) = ctrl;
-}
-
-//--------------------------------------------------------------------+
-// Private data structures
-//--------------------------------------------------------------------+
-typedef struct {
- bool valid;
- uint8_t* buffer;
- size_t len;
- size_t processed_len;
- size_t max_size;
-} xfer_ctl_t;
-
-typedef struct {
- uint32_t usb_base;
- bool ep0_tog;
- volatile uint8_t setup_pending; // SETUP arrived while EP0 completion pending in queue
- volatile bool ep0_completion_pending; // EP0 XFER_COMPLETE in event queue, not yet processed
- uint8_t pending_addr; // Address to set in ISR after Set Address status ZLP
- bool isochronous[EP_MAX][2]; // [ep][dir]
- xfer_ctl_t xfer[EP_MAX][2]; // [ep][dir]
-
- // EP0 + EP4 shared buffer: EP0 OUT(64) + EP4 OUT(64) + EP4 IN(64)
- TU_ATTR_ALIGNED(4) uint8_t ep0_buffer[EP_BUF_SIZE + EP_BUF_SIZE + EP_BUF_SIZE];
-
- // EP1-EP3: OUT(64) + IN(64)
- TU_ATTR_ALIGNED(4) uint8_t ep1_buffer[2][EP_BUF_SIZE];
- TU_ATTR_ALIGNED(4) uint8_t ep2_buffer[2][EP_BUF_SIZE];
- TU_ATTR_ALIGNED(4) uint8_t ep3_buffer[2][EP_BUF_SIZE];
-
- // EP5-EP7: OUT(64) + IN(64)
- TU_ATTR_ALIGNED(4) uint8_t ep5_buffer[2][EP_BUF_SIZE];
- TU_ATTR_ALIGNED(4) uint8_t ep6_buffer[2][EP_BUF_SIZE];
- TU_ATTR_ALIGNED(4) uint8_t ep7_buffer[2][EP_BUF_SIZE];
-} dcd_data_t;
-
-// Per-port data (support up to 2 USB ports)
-static dcd_data_t _dcd_data[2];
-
-//--------------------------------------------------------------------+
-// Buffer address helpers
-//--------------------------------------------------------------------+
-static uint8_t* ep_out_buffer(dcd_data_t* d, uint8_t ep) {
- switch (ep) {
- case 0: return &d->ep0_buffer[0];
- case 1: return d->ep1_buffer[0];
- case 2: return d->ep2_buffer[0];
- case 3: return d->ep3_buffer[0];
- case 4: return &d->ep0_buffer[EP_BUF_SIZE];
- case 5: return d->ep5_buffer[0];
- case 6: return d->ep6_buffer[0];
- case 7: return d->ep7_buffer[0];
- default: return NULL;
- }
-}
-
-static uint8_t* ep_in_buffer(dcd_data_t* d, uint8_t ep) {
- switch (ep) {
- case 0: return &d->ep0_buffer[0]; // EP0 IN uses same buffer as OUT
- case 1: return d->ep1_buffer[1];
- case 2: return d->ep2_buffer[1];
- case 3: return d->ep3_buffer[1];
- case 4: return &d->ep0_buffer[2 * EP_BUF_SIZE];
- case 5: return d->ep5_buffer[1];
- case 6: return d->ep6_buffer[1];
- case 7: return d->ep7_buffer[1];
- default: return NULL;
- }
-}
-
-// DMA base pointer (EP4 shares with EP0)
-static uint8_t* ep_dma_buffer(dcd_data_t* d, uint8_t ep) {
- switch (ep) {
- case 0: case 4: return &d->ep0_buffer[0];
- case 1: return d->ep1_buffer[0];
- case 2: return d->ep2_buffer[0];
- case 3: return d->ep3_buffer[0];
- case 5: return d->ep5_buffer[0];
- case 6: return d->ep6_buffer[0];
- case 7: return d->ep7_buffer[0];
- default: return NULL;
- }
-}
-
-// AUTO_TOG is not used (EP1-3/5-7 support it). When clear-stall resets T_TOG/
-// R_TOG to DATA0, the hardware internal toggle doesn't sync, causing mismatch
-// and bus resets. Use manual toggle (EP_CTRL ^= TOG) in ISR instead.
-
-//--------------------------------------------------------------------+
-// Private transfer helpers
-//--------------------------------------------------------------------+
-static void update_in(uint8_t rhport, uint8_t ep, bool force, bool in_isr) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- xfer_ctl_t* xfer = &d->xfer[ep][TUSB_DIR_IN];
-
- if (xfer->valid) {
- if (force || xfer->len) {
- size_t len = TU_MIN(xfer->max_size, xfer->len);
-
- // Copy data to IN buffer
- uint8_t* buf = ep_in_buffer(d, ep);
- if (len > 0 && xfer->buffer != NULL) {
- memcpy(buf, xfer->buffer, len);
- }
-
- xfer->buffer += len;
- xfer->len -= len;
- xfer->processed_len += len;
-
- EP_TLEN(base, ep) = (uint8_t) len;
-
- if (ep == 0) {
- // EP0: manual toggle
- uint8_t ctrl = EP_CTRL(base, 0);
- ctrl = (ctrl & ~(CH58X_EP_T_RES_MASK | CH58X_EP_T_TOG));
- ctrl |= CH58X_EP_T_RES_ACK;
- if (d->ep0_tog) ctrl |= CH58X_EP_T_TOG;
- EP_CTRL(base, 0) = ctrl;
- d->ep0_tog = !d->ep0_tog;
- } else if (d->isochronous[ep][TUSB_DIR_IN]) {
- ep_set_tx_response(base, ep, CH58X_EP_T_RES_TOUT);
- } else {
- ep_set_tx_response(base, ep, CH58X_EP_T_RES_ACK);
- }
- } else {
- // Transfer complete
- xfer->valid = false;
- ep_set_tx_response(base, ep, CH58X_EP_T_RES_NAK);
- if (ep == 0) d->ep0_completion_pending = true;
- dcd_event_xfer_complete(rhport, ep | TUSB_DIR_IN_MASK,
- xfer->processed_len, XFER_RESULT_SUCCESS, in_isr);
- }
- }
-}
-
-static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len, bool in_isr) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- xfer_ctl_t* xfer = &d->xfer[ep][TUSB_DIR_OUT];
-
- if (xfer->valid) {
- size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
-
- // Copy from OUT buffer
- if (len > 0 && xfer->buffer != NULL) {
- uint8_t* buf = ep_out_buffer(d, ep);
- memcpy(xfer->buffer, buf, len);
- }
-
- xfer->buffer += len;
- xfer->len -= len;
- xfer->processed_len += len;
-
- if (xfer->len == 0 || len < xfer->max_size) {
- xfer->valid = false;
- // NAK to prevent hardware from accepting next OUT before a new xfer is queued
- ep_set_rx_response(base, ep, CH58X_EP_R_RES_NAK);
- if (ep == 0) d->ep0_completion_pending = true;
- dcd_event_xfer_complete(rhport, ep, xfer->processed_len,
- XFER_RESULT_SUCCESS, in_isr);
- } else if (ep == 0) {
- // EP0 multi-packet: ensure ACK for next packet
- ep_set_rx_response(base, 0, CH58X_EP_R_RES_ACK);
- }
- }
-}
-
-//--------------------------------------------------------------------+
-// DCD API Implementation
-//--------------------------------------------------------------------+
-
-bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void) rh_init;
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = get_usb_base(rhport);
- d->usb_base = base;
-
- // Clear state
- tu_memclr(d->xfer, sizeof(d->xfer));
- tu_memclr(d->isochronous, sizeof(d->isochronous));
- d->ep0_tog = true;
- d->setup_pending = 0;
- d->ep0_completion_pending = false;
- d->pending_addr = 0;
-
- // Reset USB control register first (SDK pattern)
- USB_CTRL(base) = 0x00;
-
- // Init control registers
- USB_CTRL(base) = CH58X_UC_DEV_PU_EN | CH58X_UC_INT_BUSY | CH58X_UC_DMA_EN;
- USB_UDEV_CTRL(base) = CH58X_UD_PD_DIS | CH58X_UD_PORT_EN;
- USB_DEV_AD(base) = 0x00;
-
- // Clear all interrupt flags, then enable interrupts
- USB_INT_FG(base) = 0xFF;
- USB_INT_EN(base) = CH58X_UIE_BUS_RST | CH58X_UIE_TRANSFER | CH58X_UIE_SUSPEND;
-
- // EP0 setup (also sets EP4 DMA since they share)
- EP_DMA(base, 0) = (uint16_t)(uint32_t) &d->ep0_buffer[0];
- EP_TLEN(base, 0) = 0;
- EP_CTRL(base, 0) = CH58X_EP_R_RES_ACK | CH58X_EP_T_RES_NAK;
-
- // Enable all endpoints TX+RX
- CH58X_UEP4_1_MOD(base) = CH58X_UEP1_RX_EN | CH58X_UEP1_TX_EN |
- CH58X_UEP4_RX_EN | CH58X_UEP4_TX_EN;
- CH58X_UEP2_3_MOD(base) = CH58X_UEP2_RX_EN | CH58X_UEP2_TX_EN |
- CH58X_UEP3_RX_EN | CH58X_UEP3_TX_EN;
- CH58X_UEP567_MOD(base) = CH58X_UEP5_RX_EN | CH58X_UEP5_TX_EN |
- CH58X_UEP6_RX_EN | CH58X_UEP6_TX_EN |
- CH58X_UEP7_RX_EN | CH58X_UEP7_TX_EN;
-
- // EP1-3: DMA + manual toggle + NAK both directions
- for (uint8_t ep = 1; ep <= 3; ep++) {
- EP_DMA(base, ep) = (uint16_t)(uint32_t) ep_dma_buffer(d, ep);
- EP_TLEN(base, ep) = 0;
- EP_CTRL(base, ep) = CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
- }
-
- // EP4: no independent DMA, no auto-toggle
- EP_TLEN(base, 4) = 0;
- EP_CTRL(base, 4) = CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
-
- // EP5-7: DMA + manual toggle
- for (uint8_t ep = 5; ep <= 7; ep++) {
- EP_DMA(base, ep) = (uint16_t)(uint32_t) ep_dma_buffer(d, ep);
- EP_TLEN(base, ep) = 0;
- EP_CTRL(base, ep) = CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
- }
-
- // Set EP0 max size
- d->xfer[0][TUSB_DIR_OUT].max_size = EP_BUF_SIZE;
- d->xfer[0][TUSB_DIR_IN].max_size = EP_BUF_SIZE;
-
- dcd_connect(rhport);
- return true;
-}
-
-void dcd_int_handler(uint8_t rhport) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- uint8_t status = USB_INT_FG(base);
-
- if (status & CH58X_UIF_TRANSFER) {
- uint8_t int_st = USB_INT_ST(base);
- uint8_t ep = CH58X_INT_ST_ENDP(int_st);
- uint8_t token = CH58X_INT_ST_TOKEN(int_st);
-
- // Process regular token before SETUP to avoid losing it
- if (token != CH58X_PID_SETUP) {
- switch (token) {
- case CH58X_PID_OUT: {
- uint8_t rx_len = USB_RX_LEN(base);
- if (ep == 0) {
- // EP0: manual toggle, always process
- EP_CTRL(base, 0) ^= CH58X_EP_R_TOG;
- update_out(rhport, 0, rx_len, true);
- } else if (int_st & CH58X_UIS_TOG_OK) {
- // Toggle OK: manual toggle and process
- EP_CTRL(base, ep) ^= CH58X_EP_R_TOG;
- update_out(rhport, ep, rx_len, true);
- }
- // else: toggle mismatch, discard
- break;
- }
-
- case CH58X_PID_IN: {
- // Apply pending Set Address immediately after status ZLP
- if (ep == 0 && d->pending_addr) {
- USB_DEV_AD(base) = (USB_DEV_AD(base) & CH58X_UDA_GP_BIT) |
- (d->pending_addr & CH58X_USB_ADDR_MASK);
- d->pending_addr = 0;
- }
- if (ep != 0) {
- // Manual toggle for all non-EP0 endpoints
- EP_CTRL(base, ep) ^= CH58X_EP_T_TOG;
- }
- update_in(rhport, ep, false, true);
- break;
- }
-
- default:
- break;
- }
- USB_INT_FG(base) = CH58X_UIF_TRANSFER;
- }
-
- // SETUP_ACT is checked separately — it persists even if token field changed
- if (int_st & CH58X_UIS_SETUP_ACT) {
- // Reset toggles to DATA1, NAK both directions until stack is ready
- EP_CTRL(base, 0) = CH58X_EP_R_TOG | CH58X_EP_T_TOG |
- CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
- d->ep0_tog = true;
-
- d->pending_addr = 0;
-
- // Mark stale EP0 completion so dcd_edpt_xfer can skip it
- d->setup_pending = d->ep0_completion_pending ? 1 : 0;
- d->ep0_completion_pending = false;
- d->xfer[0][TUSB_DIR_OUT].valid = false;
- d->xfer[0][TUSB_DIR_IN].valid = false;
-
- dcd_event_setup_received(rhport, ep_out_buffer(d, 0), true);
- USB_INT_FG(base) = CH58X_UIF_TRANSFER;
- }
- }
-
- // Process bus reset: reset all endpoints immediately (matching WCH SDK pattern)
- if (status & CH58X_UIF_BUS_RST) {
- d->ep0_tog = true;
- d->setup_pending = 0;
- d->ep0_completion_pending = false;
- d->pending_addr = 0;
-
- // Reset EP0: ACK for RX (ready for SETUP), NAK for TX
- EP_CTRL(base, 0) = CH58X_EP_R_RES_ACK | CH58X_EP_T_RES_NAK;
- EP_TLEN(base, 0) = 0;
- d->xfer[0][TUSB_DIR_OUT].max_size = EP_BUF_SIZE;
- d->xfer[0][TUSB_DIR_IN].max_size = EP_BUF_SIZE;
-
- // Reset EP1-7: NAK both directions, invalidate pending transfers
- for (uint8_t ep = 1; ep < EP_MAX; ep++) {
- d->xfer[ep][TUSB_DIR_IN].valid = false;
- d->xfer[ep][TUSB_DIR_OUT].valid = false;
- EP_CTRL(base, ep) = CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
- EP_TLEN(base, ep) = 0;
- }
-
- USB_DEV_AD(base) = 0x00;
-
- tusb_speed_t speed = (USB_CTRL(base) & CH58X_UC_LOW_SPEED) ?
- TUSB_SPEED_LOW : TUSB_SPEED_FULL;
- dcd_event_bus_reset(rhport, speed, true);
-
- USB_INT_FG(base) = CH58X_UIF_BUS_RST;
- }
-
- // Process suspend/resume
- if (status & CH58X_UIF_SUSPEND) {
- dcd_event_bus_signal(rhport,
- (USB_MIS_ST(base) & CH58X_UMS_SUSPEND) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME,
- true);
- USB_INT_FG(base) = CH58X_UIF_SUSPEND;
- }
-}
-
-void dcd_int_enable(uint8_t rhport) {
- uint8_t irqn = (rhport == 0) ? CH58X_USB_IRQn : CH58X_USB2_IRQn;
- CH58X_PFIC_IENR[irqn / 32] = (1u << (irqn % 32));
-}
-
-void dcd_int_disable(uint8_t rhport) {
- uint8_t irqn = (rhport == 0) ? CH58X_USB_IRQn : CH58X_USB2_IRQn;
- CH58X_PFIC_IRER[irqn / 32] = (1u << (irqn % 32));
- __asm volatile ("fence.i");
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
- // Defer to ISR: apply address right after status ZLP is ACK'd
- _dcd_data[rhport].pending_addr = dev_addr;
- dcd_edpt_xfer(rhport, 0x80, NULL, 0, false);
-}
-
-void dcd_remote_wakeup(uint8_t rhport) {
- (void) rhport;
- // TODO: not supported
-}
-
-void dcd_connect(uint8_t rhport) {
- uint32_t base = get_usb_base(rhport);
- USB_CTRL(base) |= CH58X_UC_DEV_PU_EN;
-}
-
-void dcd_disconnect(uint8_t rhport) {
- uint32_t base = get_usb_base(rhport);
- USB_CTRL(base) &= ~CH58X_UC_DEV_PU_EN;
-}
-
-void dcd_sof_enable(uint8_t rhport, bool en) {
- (void) rhport;
- (void) en;
-}
-
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
-
- if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
- request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS) {
- // Safety net: re-apply address in case ISR path was skipped
- USB_DEV_AD(base) = (USB_DEV_AD(base) & CH58X_UDA_GP_BIT) |
- ((uint8_t)request->wValue & CH58X_USB_ADDR_MASK);
- }
-
- dcd_int_disable(rhport);
- d->ep0_completion_pending = false;
- if (d->setup_pending) {
- // SETUP already arrived — don't override its NAK with ACK
- d->setup_pending = 0;
- } else {
- ep_set_both_response(base, 0, CH58X_EP_T_RES_NAK, CH58X_EP_R_RES_ACK);
- }
- dcd_int_enable(rhport);
-}
-
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
- uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
- TU_ASSERT(ep < EP_MAX);
-
- d->isochronous[ep][dir] = (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
- uint16_t max_size = tu_edpt_packet_size(desc_ep);
- if (max_size > EP_BUF_SIZE) {
- max_size = EP_BUF_SIZE;
- }
- d->xfer[ep][dir].max_size = max_size;
-
- if (ep != 0) {
- dcd_int_disable(rhport);
- uint8_t ctrl = EP_CTRL(base, ep);
- if (dir == TUSB_DIR_OUT) {
- // Clear RX toggle to DATA0 per USB spec
- ctrl &= ~CH58X_EP_R_TOG;
- if (d->isochronous[ep][TUSB_DIR_OUT]) {
- ctrl = (ctrl & ~CH58X_EP_R_RES_MASK) | CH58X_EP_R_RES_TOUT;
- } else {
- // Start with NAK; dcd_edpt_xfer will set ACK when a transfer is submitted
- ctrl = (ctrl & ~CH58X_EP_R_RES_MASK) | CH58X_EP_R_RES_NAK;
- }
- } else {
- // Clear TX toggle to DATA0 per USB spec
- ctrl &= ~CH58X_EP_T_TOG;
- EP_TLEN(base, ep) = 0;
- ctrl = (ctrl & ~CH58X_EP_T_RES_MASK) | CH58X_EP_T_RES_NAK;
- }
- EP_CTRL(base, ep) = ctrl;
- dcd_int_enable(rhport);
- }
- return true;
-}
-
-void dcd_edpt_close_all(uint8_t rhport) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
-
- for (uint8_t ep = 1; ep < EP_MAX; ep++) {
- d->xfer[ep][TUSB_DIR_IN].valid = false;
- d->xfer[ep][TUSB_DIR_OUT].valid = false;
- d->isochronous[ep][TUSB_DIR_OUT] = false;
- d->isochronous[ep][TUSB_DIR_IN] = false;
- EP_CTRL(base, ep) = CH58X_EP_R_RES_NAK | CH58X_EP_T_RES_NAK;
- }
-}
-
-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, bool is_isr) {
- uint8_t ep = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
-
- dcd_data_t* d = &_dcd_data[rhport];
- xfer_ctl_t* xfer = &d->xfer[ep][dir];
-
- dcd_int_disable(rhport);
-
- // Skip stale EP0 arm if a new SETUP has already arrived
- if (ep == 0) {
- d->ep0_completion_pending = false;
- if (d->setup_pending) {
- d->setup_pending = 0;
- dcd_int_enable(rhport);
- return true;
- }
- }
-
- xfer->valid = true;
- xfer->buffer = buffer;
- xfer->len = total_bytes;
- xfer->processed_len = 0;
-
- if (dir == TUSB_DIR_IN) {
- update_in(rhport, ep, true, is_isr);
- } else {
- if (d->isochronous[ep][TUSB_DIR_OUT]) {
- ep_set_rx_response(d->usb_base, ep, CH58X_EP_R_RES_TOUT);
- } else {
- ep_set_rx_response(d->usb_base, ep, CH58X_EP_R_RES_ACK);
- }
- }
- dcd_int_enable(rhport);
- return true;
-}
-
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- uint8_t ep = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
-
- dcd_int_disable(rhport);
- if (ep == 0) {
- // EP0: stall both directions
- EP_CTRL(base, 0) = CH58X_EP_R_RES_STALL | CH58X_EP_T_RES_STALL |
- CH58X_EP_R_TOG | CH58X_EP_T_TOG;
- } else {
- if (dir == TUSB_DIR_OUT) {
- ep_set_rx_response(base, ep, CH58X_EP_R_RES_STALL);
- } else {
- ep_set_tx_response(base, ep, CH58X_EP_T_RES_STALL);
- }
- }
- dcd_int_enable(rhport);
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
- dcd_data_t* d = &_dcd_data[rhport];
- uint32_t base = d->usb_base;
- uint8_t ep = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
-
- dcd_int_disable(rhport);
- if (ep == 0) {
- if (dir == TUSB_DIR_OUT) {
- ep_set_rx_response(base, 0, CH58X_EP_R_RES_ACK);
- }
- } else {
- uint8_t ctrl = EP_CTRL(base, ep);
- if (dir == TUSB_DIR_OUT) {
- ctrl &= ~(CH58X_EP_R_RES_MASK | CH58X_EP_R_TOG);
- ctrl |= CH58X_EP_R_RES_ACK;
- } else {
- ctrl &= ~(CH58X_EP_T_RES_MASK | CH58X_EP_T_TOG);
- ctrl |= CH58X_EP_T_RES_NAK;
- }
- EP_CTRL(base, ep) = ctrl;
- }
- dcd_int_enable(rhport);
-}
-
-#endif /* CFG_TUD_ENABLED && TUP_USBIP_WCH_CH58X */
diff --git a/src/portable/wch/hcd_ch58x_usbfs.c b/src/portable/wch/hcd_ch58x_usbfs.c
deleted file mode 100644
index 37dd08539..000000000
--- a/src/portable/wch/hcd_ch58x_usbfs.c
+++ /dev/null
@@ -1,721 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2024 TinyUSB contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if CFG_TUH_ENABLED && defined(TUP_USBIP_WCH_CH58X) && \
- defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS
-
-#include <string.h>
-
-#include "host/hcd.h"
-#include "host/usbh.h"
-#include "host/usbh_pvt.h"
-
-#include "ch58x_usbfs_reg.h"
-
-//--------------------------------------------------------------------+
-// Configuration
-//--------------------------------------------------------------------+
-#define USBFS_MAX_PACKET_SIZE 64
-
-#define LOG_CH58X_HCD(...) TU_LOG3(__VA_ARGS__)
-
-//--------------------------------------------------------------------+
-// RX/TX buffers (must be 4-byte aligned and in lower 64KB of RAM)
-// Separate buffers for each USB port to avoid DMA conflicts
-//--------------------------------------------------------------------+
-TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[2][USBFS_MAX_PACKET_SIZE];
-TU_ATTR_ALIGNED(4) static uint8_t _tx_buf[2][USBFS_MAX_PACKET_SIZE];
-
-//--------------------------------------------------------------------+
-// USB base address selection by rhport
-//--------------------------------------------------------------------+
-static inline uint32_t _get_usb_base(uint8_t rhport) {
- return (rhport == 0) ? CH58X_USB_BASE : CH58X_USB2_BASE;
-}
-
-//--------------------------------------------------------------------+
-// Endpoint record
-//--------------------------------------------------------------------+
-typedef struct {
- bool configured;
- uint8_t dev_addr;
- uint8_t ep_addr;
- uint8_t max_packet_size;
- uint8_t xfer_type;
- uint8_t data_toggle; // 0=DATA0, 1=DATA1
- bool is_nak_pending;
- uint16_t buflen;
- uint8_t* buf;
-} hcd_edpt_t;
-
-static hcd_edpt_t _edpt_list[CFG_TUH_DEVICE_MAX * 6] = {};
-
-//--------------------------------------------------------------------+
-// Current transfer state (only one transfer at a time per root port)
-//--------------------------------------------------------------------+
-typedef struct {
- volatile bool is_busy;
- uint8_t rhport;
- uint8_t dev_addr;
- uint8_t ep_addr;
- uint32_t start_ms;
- uint8_t* buffer;
- uint16_t bufferlen;
- uint16_t xferred_len;
- bool nak_pending;
-} hcd_xfer_t;
-
-// One transfer state per root port (indexed by rhport).
-static volatile hcd_xfer_t _current_xfer[2] = {};
-
-//--------------------------------------------------------------------+
-// Per-port state
-//--------------------------------------------------------------------+
-typedef struct {
- uint32_t usb_base;
- bool int_enabled;
- bool int_state_before_reset;
-} hcd_port_t;
-
-static hcd_port_t _port_data[2] = {};
-
-//--------------------------------------------------------------------+
-// Endpoint record management
-//--------------------------------------------------------------------+
-static hcd_edpt_t* _get_edpt(uint8_t dev_addr, uint8_t ep_addr) {
- for (size_t i = 0; i < TU_ARRAY_SIZE(_edpt_list); i++) {
- hcd_edpt_t* e = &_edpt_list[i];
- if (e->configured && e->dev_addr == dev_addr && e->ep_addr == ep_addr) {
- return e;
- }
- }
- return NULL;
-}
-
-static hcd_edpt_t* _alloc_edpt(void) {
- for (size_t i = 0; i < TU_ARRAY_SIZE(_edpt_list); i++) {
- if (!_edpt_list[i].configured) {
- return &_edpt_list[i];
- }
- }
- return NULL;
-}
-
-static hcd_edpt_t* _add_edpt(uint8_t dev_addr, uint8_t ep_addr,
- uint16_t max_packet_size, uint8_t xfer_type) {
- hcd_edpt_t* e = _alloc_edpt();
- TU_ASSERT(e != NULL, NULL);
-
- e->dev_addr = dev_addr;
- e->ep_addr = ep_addr;
- e->max_packet_size = (uint8_t) TU_MIN(max_packet_size, USBFS_MAX_PACKET_SIZE);
- e->xfer_type = xfer_type;
- e->data_toggle = 0;
- e->is_nak_pending = false;
- e->buflen = 0;
- e->buf = NULL;
- e->configured = true;
-
- return e;
-}
-
-static hcd_edpt_t* _get_or_add_edpt(uint8_t dev_addr, uint8_t ep_addr,
- uint16_t max_packet_size, uint8_t xfer_type) {
- hcd_edpt_t* e = _get_edpt(dev_addr, ep_addr);
- if (e != NULL) return e;
- return _add_edpt(dev_addr, ep_addr, max_packet_size, xfer_type);
-}
-
-static void _remove_edpts_for_device(uint8_t dev_addr) {
- for (size_t i = 0; i < TU_ARRAY_SIZE(_edpt_list); i++) {
- if (_edpt_list[i].configured && _edpt_list[i].dev_addr == dev_addr) {
- _edpt_list[i].configured = false;
- }
- }
-}
-
-//--------------------------------------------------------------------+
-// Low-level hardware helpers
-//--------------------------------------------------------------------+
-
-// Busywait delay (approximate microseconds at ~60MHz)
-TU_ATTR_ALWAYS_INLINE static inline void _delay_loops(uint32_t count) {
- volatile uint32_t c = count / 3;
- if (c == 0) return;
- while (c-- != 0) {}
-}
-
-static void _hw_init_host(uint8_t rhport, bool enabled) {
- uint32_t base = _port_data[rhport].usb_base;
-
- if (!enabled) {
- // Reset SIE when disabling
- CH58X_USB_CTRL(base) = CH58X_UC_RESET_SIE | CH58X_UC_CLR_ALL;
- _delay_loops(600); // ~10us at 60MHz
- CH58X_USB_CTRL(base) = 0;
- return;
- }
-
- // host mode, pull-down enabled, clear addr
- CH58X_USB_CTRL(base) = CH58X_UC_HOST_MODE;
- while (!(CH58X_USB_CTRL(base) & CH58X_UC_HOST_MODE)) {}
- CH58X_UHOST_CTRL(base) = 0;
- CH58X_USB_DEV_AD(base) = 0x00;
-
- // EP2 RX (IN), EP3 TX (OUT/SETUP)
- CH58X_UH_EP_MOD(base) = CH58X_UH_EP_TX_EN | CH58X_UH_EP_RX_EN;
-
- // DMA: 16-bit address, lower 64KB only
- CH58X_UH_RX_DMA(base) = (uint16_t)(uint32_t)_rx_buf[rhport];
- CH58X_UH_TX_DMA(base) = (uint16_t)(uint32_t)_tx_buf[rhport];
-
- CH58X_UH_RX_CTRL(base) = 0x00;
- CH58X_UH_TX_CTRL(base) = 0x00;
-
- CH58X_USB_CTRL(base) = CH58X_UC_HOST_MODE | CH58X_UC_INT_BUSY | CH58X_UC_DMA_EN;
- CH58X_UH_SETUP(base) = CH58X_UH_SOF_EN;
-
- CH58X_USB_INT_FG(base) = 0xFF; // clear all flags
- CH58X_USB_INT_EN(base) = CH58X_UIE_TRANSFER | CH58X_UIE_DETECT;
-}
-
-static bool _hw_start_xfer(uint8_t rhport, uint8_t pid, uint8_t ep_addr, uint8_t data_toggle) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("_hw_start_xfer(pid=0x%02x, ep=0x%02x, tog=%d)\r\n", pid, ep_addr, data_toggle);
-
- // Workaround: small delay for low-speed devices
- bool is_lowspeed = tuh_speed_get(_current_xfer[rhport].dev_addr) == TUSB_SPEED_LOW;
- if (is_lowspeed) {
- _delay_loops(60000000 / 1000000 * 40); // ~40us at 60MHz
- }
-
- // Set toggle controls (same as SDK: R8_UH_RX_CTRL = R8_UH_TX_CTRL = tog)
- uint8_t tog_ctrl = (data_toggle != 0) ? CH58X_UH_T_TOG : 0;
- CH58X_UH_TX_CTRL(base) = tog_ctrl;
- CH58X_UH_RX_CTRL(base) = (data_toggle != 0) ? CH58X_UH_R_TOG : 0;
-
- uint8_t pid_endp = (pid << 4) | (tu_edpt_number(ep_addr) & 0x0F);
-
- // clear flag, enable int, then set PID to start transfer
- CH58X_USB_INT_FG(base) = CH58X_UIF_TRANSFER;
- CH58X_USB_INT_EN(base) |= CH58X_UIE_TRANSFER;
- CH58X_UH_EP_PID(base) = pid_endp;
-
- return true;
-}
-
-static void _hw_set_device_addr(uint8_t rhport, uint8_t dev_addr) {
- uint32_t base = _port_data[rhport].usb_base;
- CH58X_USB_DEV_AD(base) = (CH58X_USB_DEV_AD(base) & CH58X_UDA_GP_BIT) |
- (dev_addr & CH58X_USB_ADDR_MASK);
-}
-
-static void _hw_set_speed(uint8_t rhport, tusb_speed_t speed) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("_hw_set_speed(%s)\r\n",
- speed == TUSB_SPEED_FULL ? "Full" : "Low");
-
- if (speed == TUSB_SPEED_LOW) {
- CH58X_USB_CTRL(base) |= CH58X_UC_LOW_SPEED;
- CH58X_UHOST_CTRL(base) |= CH58X_UH_LOW_SPEED;
- } else {
- CH58X_USB_CTRL(base) &= ~CH58X_UC_LOW_SPEED;
- CH58X_UHOST_CTRL(base) &= ~CH58X_UH_LOW_SPEED;
- CH58X_UH_SETUP(base) &= ~CH58X_UH_PRE_PID_EN;
- }
-}
-
-static void _hw_set_addr_speed(uint8_t rhport, uint8_t dev_addr) {
- _hw_set_device_addr(rhport, dev_addr);
-
- tusb_speed_t rhport_speed = hcd_port_speed_get(rhport);
- tusb_speed_t dev_speed = tuh_speed_get(dev_addr);
- _hw_set_speed(rhport, dev_speed);
-
- // FS root + LS device: hub uses PRE PID, clear LS on host ctrl
- if (rhport_speed == TUSB_SPEED_FULL && dev_speed == TUSB_SPEED_LOW) {
- uint32_t base = _port_data[rhport].usb_base;
- CH58X_UHOST_CTRL(base) &= ~CH58X_UH_LOW_SPEED;
- }
-}
-
-static bool _hw_device_attached(uint8_t rhport) {
- uint32_t base = _port_data[rhport].usb_base;
- return (CH58X_USB_MIS_ST(base) & CH58X_UMS_DEV_ATTACH) != 0;
-}
-
-//--------------------------------------------------------------------+
-// NAK retry callback
-//--------------------------------------------------------------------+
-static void _xfer_retry(void* param) {
- LOG_CH58X_HCD("_xfer_retry()\r\n");
- hcd_edpt_t* edpt = (hcd_edpt_t*)param;
-
- // Find which rhport this endpoint belongs to by checking both ports
- for (uint8_t rp = 0; rp < 2; rp++) {
- if (_current_xfer[rp].nak_pending) {
- _current_xfer[rp].nak_pending = false;
- edpt->is_nak_pending = false;
-
- uint8_t dev_addr = edpt->dev_addr;
- uint8_t ep_addr = edpt->ep_addr;
- uint16_t buflen = edpt->buflen;
- uint8_t* buf = edpt->buf;
-
- // Check if endpoint is still valid
- hcd_edpt_t* current = _get_edpt(dev_addr, ep_addr);
- if (current) {
- hcd_edpt_xfer(rp, dev_addr, ep_addr, buf, buflen);
- }
- break;
- }
- }
-}
-
-//--------------------------------------------------------------------+
-// HCD API: Controller
-//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
- (void)rh_init;
-
- _port_data[rhport].usb_base = _get_usb_base(rhport);
- _port_data[rhport].int_enabled = false;
-
- // Clear endpoint records only on first init to avoid wiping state of the other rhport
- static bool _first_init = true;
- if (_first_init) {
- tu_memclr(_edpt_list, sizeof(_edpt_list));
- _first_init = false;
- }
- _current_xfer[rhport] = (const hcd_xfer_t){0};
-
- _hw_init_host(rhport, true);
-
- return true;
-}
-
-bool hcd_deinit(uint8_t rhport) {
- _hw_init_host(rhport, false);
- return true;
-}
-
-uint32_t hcd_frame_number(uint8_t rhport) {
- (void)rhport;
- return tusb_time_millis_api();
-}
-
-void hcd_int_enable(uint8_t rhport) {
- uint8_t irqn = (rhport == 0) ? CH58X_USB_IRQn : CH58X_USB2_IRQn;
- CH58X_PFIC_IENR[irqn / 32] = (1u << (irqn % 32));
- _port_data[rhport].int_enabled = true;
-}
-
-void hcd_int_disable(uint8_t rhport) {
- uint8_t irqn = (rhport == 0) ? CH58X_USB_IRQn : CH58X_USB2_IRQn;
- CH58X_PFIC_IRER[irqn / 32] = (1u << (irqn % 32));
- __asm volatile("fence.i");
- _port_data[rhport].int_enabled = false;
-}
-
-//--------------------------------------------------------------------+
-// HCD API: Port
-//--------------------------------------------------------------------+
-bool hcd_port_connect_status(uint8_t rhport) {
- return _hw_device_attached(rhport);
-}
-
-tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
- uint32_t base = _port_data[rhport].usb_base;
- if (CH58X_USB_MIS_ST(base) & CH58X_UMS_DM_LEVEL) {
- return TUSB_SPEED_LOW;
- }
- return TUSB_SPEED_FULL;
-}
-
-void hcd_port_reset(uint8_t rhport) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("hcd_port_reset()\r\n");
-
- _port_data[rhport].int_state_before_reset = _port_data[rhport].int_enabled;
- hcd_int_disable(rhport);
-
- _hw_set_device_addr(rhport, 0x00);
-
- // Disable port and default to full-speed before reset (matches SDK ResetRootHubPort)
- CH58X_UHOST_CTRL(base) &= ~CH58X_UH_PORT_EN;
- _hw_set_speed(rhport, TUSB_SPEED_FULL);
-
- // Start bus reset (clear low-speed bit simultaneously)
- CH58X_UHOST_CTRL(base) = (CH58X_UHOST_CTRL(base) & ~CH58X_UH_LOW_SPEED) | CH58X_UH_BUS_RESET;
-}
-
-void hcd_port_reset_end(uint8_t rhport) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("hcd_port_reset_end()\r\n");
-
- // End bus reset
- CH58X_UHOST_CTRL(base) &= ~CH58X_UH_BUS_RESET;
- tusb_time_delay_ms_api(2);
-
- // Detect speed and configure
- if ((CH58X_UHOST_CTRL(base) & CH58X_UH_PORT_EN) == 0) {
- if (hcd_port_speed_get(rhport) == TUSB_SPEED_LOW) {
- _hw_set_speed(rhport, TUSB_SPEED_LOW);
- }
- }
-
- // Enable port and SOF
- CH58X_UHOST_CTRL(base) |= CH58X_UH_PORT_EN;
- CH58X_UH_SETUP(base) |= CH58X_UH_SOF_EN;
-
- // Suppress stale detect event
- CH58X_USB_INT_FG(base) = CH58X_UIF_DETECT;
-
- if (_port_data[rhport].int_state_before_reset) {
- hcd_int_enable(rhport);
- }
-}
-
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
- (void)rhport;
- LOG_CH58X_HCD("hcd_device_close(dev=0x%02x)\r\n", dev_addr);
- _remove_edpts_for_device(dev_addr);
-}
-
-//--------------------------------------------------------------------+
-// HCD API: Endpoint
-//--------------------------------------------------------------------+
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc) {
- uint8_t ep_addr = ep_desc->bEndpointAddress;
- uint8_t ep_num = tu_edpt_number(ep_addr);
- uint16_t max_packet_size = ep_desc->wMaxPacketSize;
- uint8_t xfer_type = ep_desc->bmAttributes.xfer;
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("hcd_edpt_open(dev=0x%02x, ep=0x%02x, mps=%d, type=%d)\r\n",
- dev_addr, ep_addr, max_packet_size, xfer_type);
-
- // Wait for any pending transfer
- uint32_t t0 = tusb_time_millis_api();
- while (_current_xfer[rhport].is_busy) {
- if (tusb_time_millis_api() - t0 > 200) {
- _current_xfer[rhport].is_busy = false;
- break;
- }
- }
-
- if (ep_num == 0) {
- TU_ASSERT(_get_or_add_edpt(dev_addr, 0x00, max_packet_size, xfer_type) != NULL, false);
- TU_ASSERT(_get_or_add_edpt(dev_addr, 0x80, max_packet_size, xfer_type) != NULL, false);
- } else {
- TU_ASSERT(_get_or_add_edpt(dev_addr, ep_addr, max_packet_size, xfer_type) != NULL, false);
- }
-
- // Ensure port is enabled with SOF
- CH58X_UHOST_CTRL(base) |= CH58X_UH_PORT_EN;
- CH58X_UH_SETUP(base) |= CH58X_UH_SOF_EN;
-
- _hw_set_addr_speed(rhport, dev_addr);
-
- return true;
-}
-
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr,
- uint8_t* buffer, uint16_t buflen) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("hcd_edpt_xfer(dev=0x%02x, ep=0x%02x, len=%d)\r\n",
- dev_addr, ep_addr, buflen);
-
- // Wait for any pending transfer (with 200ms timeout to avoid deadlock on disconnect)
- uint32_t t0 = tusb_time_millis_api();
- while (_current_xfer[rhport].is_busy) {
- if (tusb_time_millis_api() - t0 > 200) {
- _current_xfer[rhport].is_busy = false;
- return false;
- }
- }
- _current_xfer[rhport].is_busy = true;
-
- hcd_edpt_t* edpt = _get_edpt(dev_addr, ep_addr);
- TU_ASSERT(edpt != NULL);
-
- _hw_set_addr_speed(rhport, dev_addr);
-
- _current_xfer[rhport].rhport = rhport;
- _current_xfer[rhport].dev_addr = dev_addr;
- _current_xfer[rhport].ep_addr = ep_addr;
- _current_xfer[rhport].buffer = buffer;
- _current_xfer[rhport].bufferlen = buflen;
- _current_xfer[rhport].start_ms = tusb_time_millis_api();
- _current_xfer[rhport].xferred_len = 0;
- _current_xfer[rhport].nak_pending = false;
-
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- // IN transfer: host receives data
- return _hw_start_xfer(rhport, CH58X_USB_PID_IN, ep_addr, edpt->data_toggle);
- } else {
- // OUT transfer: host sends data
- uint16_t copylen = TU_MIN(edpt->max_packet_size, buflen);
- CH58X_UH_TX_LEN(base) = (uint8_t)copylen;
- memcpy(_tx_buf[rhport], buffer, copylen);
- return _hw_start_xfer(rhport, CH58X_USB_PID_OUT, ep_addr, edpt->data_toggle);
- }
-}
-
-bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
- (void)rhport;
- (void)dev_addr;
- (void)ep_addr;
- return false;
-}
-
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
- uint32_t base = _port_data[rhport].usb_base;
-
- LOG_CH58X_HCD("hcd_setup_send(dev=0x%02x)\r\n", dev_addr);
-
- // Wait for any pending transfer
- uint32_t t0 = tusb_time_millis_api();
- while (_current_xfer[rhport].is_busy) {
- if (tusb_time_millis_api() - t0 > 200) {
- _current_xfer[rhport].is_busy = false;
- return false;
- }
- }
- _current_xfer[rhport].is_busy = true;
-
- _hw_set_addr_speed(rhport, dev_addr);
-
- hcd_edpt_t* edpt_tx = _get_edpt(dev_addr, 0x00);
- hcd_edpt_t* edpt_rx = _get_edpt(dev_addr, 0x80);
- TU_ASSERT(edpt_tx != NULL, false);
- TU_ASSERT(edpt_rx != NULL, false);
-
- // SETUP always starts with DATA0; after SETUP, IN data starts with DATA1
- edpt_tx->data_toggle = 0;
- edpt_rx->data_toggle = 1;
-
- memcpy(_tx_buf[rhport], setup_packet, 8);
- CH58X_UH_TX_LEN(base) = 8;
-
- _current_xfer[rhport].rhport = rhport;
- _current_xfer[rhport].dev_addr = dev_addr;
- _current_xfer[rhport].ep_addr = 0x00; // SETUP always targets EP0 OUT
- _current_xfer[rhport].start_ms = tusb_time_millis_api();
- _current_xfer[rhport].buffer = _tx_buf[rhport];
- _current_xfer[rhport].bufferlen = 8;
- _current_xfer[rhport].xferred_len = 0;
- _current_xfer[rhport].nak_pending = false;
-
- _hw_start_xfer(rhport, CH58X_USB_PID_SETUP, 0, 0);
-
- return true;
-}
-
-bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
- (void) rhport;
-
- LOG_CH58X_HCD("hcd_edpt_clear_stall(dev=0x%02x, ep=0x%02x)\r\n", dev_addr, ep_addr);
- hcd_edpt_t* edpt = _get_edpt(dev_addr, ep_addr);
- if (edpt != NULL) {
- edpt->data_toggle = 0;
- }
-
- return true;
-}
-
-//--------------------------------------------------------------------+
-// Interrupt Handler
-//--------------------------------------------------------------------+
-void hcd_int_handler(uint8_t rhport, bool in_isr) {
- uint32_t base = _port_data[rhport].usb_base;
-
- //-- Device attach/detach detection --
- if (CH58X_USB_INT_FG(base) & CH58X_UIF_DETECT) {
- CH58X_USB_INT_FG(base) = CH58X_UIF_DETECT;
-
- bool attached = _hw_device_attached(rhport);
- LOG_CH58X_HCD("hcd_int: detect, attached=%d\r\n", attached);
-
- if (attached) {
- hcd_event_device_attach(rhport, in_isr);
- } else {
- // Stop any ongoing hardware transfer before reporting removal
- CH58X_UH_EP_PID(base) = 0x00;
- _current_xfer[rhport].is_busy = false;
- _current_xfer[rhport].nak_pending = false;
- hcd_event_device_remove(rhport, in_isr);
- }
- return;
- }
-
- //-- Transfer complete --
- if (CH58X_USB_INT_FG(base) & CH58X_UIF_TRANSFER) {
- // Read PID/endpoint before stopping (must read first!)
- uint8_t pid_endp = CH58X_UH_EP_PID(base);
- uint8_t int_st = CH58X_USB_INT_ST(base);
- uint8_t dev_addr = CH58X_USB_DEV_AD(base) & CH58X_USB_ADDR_MASK;
-
- // Stop USB transaction immediately (SDK: R8_UH_EP_PID = 0x00)
- CH58X_UH_EP_PID(base) = 0x00;
-
- // Disable transfer interrupt (re-enabled when next transfer starts)
- CH58X_USB_INT_EN(base) &= ~CH58X_UIE_TRANSFER;
-
- uint8_t request_pid = pid_endp >> 4;
- uint8_t response_pid = int_st & CH58X_UIS_H_RES_MASK;
- uint8_t ep_addr = pid_endp & 0x0F;
- if (request_pid == CH58X_USB_PID_IN) {
- ep_addr |= 0x80;
- }
-
- LOG_CH58X_HCD("hcd_int: xfer pid=0x%02x ep=0x%02x resp=0x%02x\r\n",
- request_pid, ep_addr, response_pid);
-
- hcd_edpt_t* edpt = _get_edpt(dev_addr, ep_addr);
- if (edpt == NULL) {
- // Unknown endpoint, discard
- LOG_CH58X_HCD("hcd_int: unknown edpt dev=0x%02x ep=0x%02x\r\n", dev_addr, ep_addr);
- _current_xfer[rhport].is_busy = false;
- CH58X_USB_INT_FG(base) = CH58X_UIF_TRANSFER;
- return;
- }
-
- // Check toggle match - SDK uses R8_USB_INT_ST & RB_UIS_TOG_OK
- if (int_st & CH58X_UIS_TOG_OK) {
- edpt->data_toggle ^= 0x01;
-
- switch (request_pid) {
- case CH58X_USB_PID_SETUP:
- case CH58X_USB_PID_OUT: {
- uint8_t tx_len = CH58X_UH_TX_LEN(base);
- _current_xfer[rhport].bufferlen -= tx_len;
- _current_xfer[rhport].xferred_len += tx_len;
-
- if (_current_xfer[rhport].bufferlen == 0) {
- LOG_CH58X_HCD("OUT/SETUP complete, %d bytes\r\n", _current_xfer[rhport].xferred_len);
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr,
- _current_xfer[rhport].xferred_len, XFER_RESULT_SUCCESS, in_isr);
- } else {
- // Multi-packet OUT: send next chunk
- _current_xfer[rhport].buffer += tx_len;
- uint16_t copylen = TU_MIN(edpt->max_packet_size, _current_xfer[rhport].bufferlen);
- memcpy(_tx_buf[rhport], _current_xfer[rhport].buffer, copylen);
- CH58X_UH_TX_LEN(base) = (uint8_t)copylen;
- _hw_start_xfer(rhport, CH58X_USB_PID_OUT, ep_addr, edpt->data_toggle);
- }
- break;
- }
-
- case CH58X_USB_PID_IN: {
- uint8_t rx_len = CH58X_USB_RX_LEN(base);
- _current_xfer[rhport].xferred_len += rx_len;
- uint16_t xferred = _current_xfer[rhport].xferred_len;
-
- if (rx_len > 0 && _current_xfer[rhport].buffer != NULL) {
- memcpy(_current_xfer[rhport].buffer, _rx_buf[rhport], rx_len);
- _current_xfer[rhport].buffer += rx_len;
- }
-
- if ((rx_len < edpt->max_packet_size) || (xferred == _current_xfer[rhport].bufferlen)) {
- // Short packet or transfer complete
- LOG_CH58X_HCD("IN complete, %d bytes\r\n", xferred);
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, xferred,
- XFER_RESULT_SUCCESS, in_isr);
- } else {
- // More data expected
- _hw_start_xfer(rhport, CH58X_USB_PID_IN, ep_addr, edpt->data_toggle);
- }
- break;
- }
-
- default: {
- LOG_CH58X_HCD("unexpected PID 0x%02x\r\n", request_pid);
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
- break;
- }
- }
- } else {
- // Toggle mismatch, check response PID
- if (response_pid == CH58X_USB_PID_STALL) {
- LOG_CH58X_HCD("STALL response\r\n");
- edpt->data_toggle = 0;
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_STALLED, in_isr);
- } else if (response_pid == CH58X_USB_PID_NAK) {
- LOG_CH58X_HCD("NAK response\r\n");
- // For interrupt endpoints, treat NAK as a successful 0-byte poll so
- // that upper layers can reschedule based on the endpoint interval.
- if (edpt->xfer_type == TUSB_XFER_INTERRUPT) {
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, in_isr);
- } else {
- // For non-interrupt endpoints, schedule retry via deferred callback.
- _current_xfer[rhport].is_busy = false;
- _current_xfer[rhport].nak_pending = true;
-
- edpt->is_nak_pending = true;
- edpt->buflen = _current_xfer[rhport].bufferlen;
- edpt->buf = _current_xfer[rhport].buffer;
-
- hcd_event_t event = {
- .rhport = rhport,
- .dev_addr = dev_addr,
- .event_id = USBH_EVENT_FUNC_CALL,
- .func_call = {
- .func = _xfer_retry,
- .param = edpt
- }
- };
- hcd_event_handler(&event, in_isr);
- }
- } else if (response_pid == CH58X_USB_PID_DATA0 || response_pid == CH58X_USB_PID_DATA1) {
- LOG_CH58X_HCD("toggle mismatch, DATA0/1 rx_len=%d\r\n", CH58X_USB_RX_LEN(base));
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
- } else {
- LOG_CH58X_HCD("unexpected response 0x%02x\r\n", response_pid);
- _current_xfer[rhport].is_busy = false;
- hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr);
- }
- }
-
- // Clear transfer flag
- CH58X_USB_INT_FG(base) = CH58X_UIF_TRANSFER;
- }
-}
-
-#endif /* CFG_TUH_ENABLED && TUP_USBIP_WCH_CH58X */