summaryrefslogtreecommitdiff
path: root/src/portable
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2025-07-11 15:47:55 +0200
committerHiFiPhile <[email protected]>2025-07-11 15:47:55 +0200
commitf074815405f9c510f3f1eb57f3cbdab40e9e1caa (patch)
tree27157de2ae8ef662974a9d87a8119c12f3fbdffb /src/portable
parent69f6b57772ad4f2923dcf48aabaaba044085c4b0 (diff)
parent29c2af7651f9075a63a036e94c7bbe3d0d00c31a (diff)
Merge branch 'master' into feature/usbtmc_vendor-specific
Diffstat (limited to 'src/portable')
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c69
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c4
-rw-r--r--src/portable/microchip/samd/hcd_samd.c765
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c82
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md116
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py2
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h82
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c4
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c2
-rw-r--r--src/portable/wch/hcd_ch32_usbfs.c625
11 files changed, 1655 insertions, 100 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index bb33200f2..971dbd62e 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -28,9 +28,9 @@
#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
-#include <stdatomic.h>
#include "host/hcd.h"
#include "host/usbh.h"
+#include "host/usbh_pvt.h"
//--------------------------------------------------------------------+
//
@@ -233,7 +233,7 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
- atomic_flag busy; // busy transferring
+ bool busy_lock; // busy transferring
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@@ -327,7 +327,9 @@ TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data
}
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
- if ( _hcd_data.peraddr == data ) return; // no need to change address
+ if (_hcd_data.peraddr == data) {
+ return; // no need to change address
+ }
_hcd_data.peraddr = data;
reg_write(rhport, PERADDR_ADDR, data, in_isr);
@@ -373,7 +375,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint
static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
uint8_t hirq;
- uint8_t const reg = RCVVFIFO_ADDR;
+ const uint8_t reg = RCVVFIFO_ADDR;
max3421_spi_lock(rhport, in_isr);
@@ -389,7 +391,7 @@ static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool
//--------------------------------------------------------------------+
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
- uint8_t const is_out = 1-ep_dir;
+ const uint8_t is_out = 1-ep_dir;
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
// control endpoint is bi-direction (skip check)
@@ -727,8 +729,8 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@@ -744,8 +746,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_generic(rhport, ep, true, false);
}
@@ -781,8 +792,17 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
+ bool has_xfer = false;
+
+ usbh_spin_lock(false);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(false);
+
// carry out transfer if not busy
- if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_setup(rhport, ep, false);
}
@@ -848,8 +868,8 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
}
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
- uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
- uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
+ const uint8_t ep_dir = 1 - ep->hxfr_bm.is_out;
+ const uint8_t ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
// save data toggle
if (ep_dir) {
@@ -867,7 +887,9 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
xact_generic(rhport, next_ep, true, in_isr);
}else {
// no more pending
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
}
@@ -906,7 +928,9 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xact_generic(rhport, next_ep, true, in_isr);
} else {
// no more pending in this frame -> clear busy
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
return;
@@ -997,8 +1021,8 @@ void print_hirq(uint8_t hirq) {
// Interrupt handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
- if (!hirq) return;
-// print_hirq(hirq);
+ if (!hirq) { return; }
+ // print_hirq(hirq);
if (hirq & HIRQ_FRAME_IRQ) {
_hcd_data.frame_count++;
@@ -1017,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
- if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
- xact_generic(rhport, ep_retry, true, in_isr);
+ if (ep_retry != NULL) {
+ bool has_xfer = false;
+
+ usbh_spin_lock(in_isr);
+ if (!_hcd_data.busy_lock) {
+ _hcd_data.busy_lock = true;
+ has_xfer = true;
+ }
+ usbh_spin_unlock(in_isr);
+
+ if (has_xfer) {
+ xact_generic(rhport, ep_retry, true, in_isr);
+ }
}
}
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index a716dc24c..244f5a2d4 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -239,7 +239,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
usbmode |= USBMODE_CM_DEVICE;
dcd_reg->USBMODE = usbmode;
+#ifdef CFG_TUD_CI_HS_VBUS_CHARGE
+ dcd_reg->OTGSC = OTGSC_VBUS_CHARGE | OTGSC_OTG_TERMINATION;
+#else
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
+#endif
#if !TUD_OPT_HIGH_SPEED
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
diff --git a/src/portable/microchip/samd/hcd_samd.c b/src/portable/microchip/samd/hcd_samd.c
new file mode 100644
index 000000000..1f4b2b233
--- /dev/null
+++ b/src/portable/microchip/samd/hcd_samd.c
@@ -0,0 +1,765 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 ChrisDeadman
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && \
+ !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) && \
+ (CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
+ CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X || \
+ CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21)
+
+#include "host/hcd.h"
+#include "sam.h"
+
+/*------------------------------------------------------------------*/
+/* MACRO TYPEDEF CONSTANT ENUM
+ *------------------------------------------------------------------*/
+#define USB_HOST_PTYPE_DIS 0x0
+#define USB_HOST_PTYPE_CTRL 0x1
+#define USB_HOST_PTYPE_ISO 0x2
+#define USB_HOST_PTYPE_BULK 0x3
+#define USB_HOST_PTYPE_INT 0x4
+#define USB_HOST_PTYPE_EXT 0x5
+
+#define USB_HOST_PCFG_PTOKEN_SETUP 0x0
+#define USB_HOST_PCFG_PTOKEN_IN 0x1
+#define USB_HOST_PCFG_PTOKEN_OUT 0x2
+
+#define USB_PCKSIZE_ENUM(size) \
+ ((size) >= 1024 ? 7 \
+ : (size) >= 1023 ? 7 \
+ : (size) > 256 ? 6 \
+ : (size) > 128 ? 5 \
+ : (size) > 64 ? 4 \
+ : (size) > 32 ? 3 \
+ : (size) > 16 ? 2 \
+ : (size) > 8 ? 1 \
+ : 0)
+
+// Uncomment to use fake frame number.
+// Low-Speed devices stall FNUM during enumeration :/
+// #define HCD_SAMD_FAKE_FNUM
+
+typedef struct {
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint16_t max_packet_size;
+ uint16_t xfer_length;
+ uint16_t xfer_remaining;
+} usb_pipe_status_t;
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile UsbHostDescriptor usb_pipe_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile usb_pipe_status_t usb_pipe_status_table[USB_PIPE_NUM];
+
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static volatile uint32_t fake_fnum;
+
+static uint8_t samd_configure_pipe(uint8_t dev_addr, uint8_t ep_addr)
+{
+ uint8_t pipe;
+ uint8_t token;
+ volatile usb_pipe_status_t* pipe_status;
+ bool same_addr = false;
+ bool same_ep_addr = false;
+
+ // evaluate pipe token
+ token = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_HOST_PCFG_PTOKEN_IN
+ : tu_edpt_number(ep_addr) == 0 ? USB_HOST_PCFG_PTOKEN_SETUP
+ : USB_HOST_PCFG_PTOKEN_OUT;
+
+ TU_LOG3("samd_configure_pipe(token=%02X, dev_addr=%02X, ep_addr=%02X)=", token, dev_addr, ep_addr);
+
+ // find already allocated pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ same_addr = (pipe_status->dev_addr == dev_addr);
+ same_ep_addr = (tu_edpt_number(pipe_status->ep_addr) == tu_edpt_number(ep_addr));
+ if (same_ep_addr && (same_addr || (tu_edpt_number(ep_addr) == 0))) {
+ break;
+ }
+ }
+
+ // allocate from pool of free pipes
+ if (pipe >= USB_PIPE_NUM) {
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ // found a free pipe
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ break;
+ }
+ }
+ }
+
+ // no pipe available :(
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return pipe;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer should be in progress
+ TU_ASSERT(((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_DIS) ||
+ USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1),
+ USB_PIPE_NUM);
+
+ // update addr and ep_addr
+ pipe_status->dev_addr = dev_addr;
+ pipe_status->ep_addr = ep_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PDADDR = dev_addr;
+ usb_pipe_table[pipe].HostDescBank[0].CTRL_PIPE.bit.PEPNUM = tu_edpt_number(ep_addr);
+
+ // token specific configuration
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTOKEN = token;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ if (token == USB_HOST_PCFG_PTOKEN_SETUP) {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TXSTP;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else if (token == USB_HOST_PCFG_PTOKEN_IN) {
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ } else {
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_TRFAIL;
+ USB->HOST.HostPipe[pipe].PINTENSET.reg = USB_HOST_PINTENSET_PERR;
+ }
+
+ return pipe;
+}
+
+static void samd_free_pipe(uint8_t pipe)
+{
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->dev_addr = UINT8_MAX;
+ pipe_status->ep_addr = UINT8_MAX;
+ pipe_status->max_packet_size = 0;
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ memset((uint8_t*)(uintptr_t) &usb_pipe_table[pipe], 0, sizeof(usb_pipe_table[pipe]));
+}
+
+static void samd_free_all_pipes(void)
+{
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ samd_free_pipe(pipe);
+ }
+}
+
+static bool samd_on_xfer(uint8_t pipe, xfer_result_t xfer_result)
+{
+ uint16_t xfer_delta;
+ bool xfer_complete;
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+
+ // freeze the pipe
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+
+ // get number of transferred bytes
+ if (xfer_result == XFER_RESULT_SUCCESS) {
+ xfer_delta = usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
+ } else {
+ xfer_delta = 0;
+ }
+
+ TU_LOG3("samd_on_xfer(%d, result=%d, xdelta=%d, rem=%d)\r\n", xfer_result, pipe, xfer_delta, pipe_status->xfer_remaining);
+
+ // update pipe status
+ if (xfer_delta > pipe_status->xfer_remaining) {
+ xfer_delta = pipe_status->xfer_remaining;
+ }
+ pipe_status->xfer_remaining -= xfer_delta;
+ pipe_status->xfer_length += xfer_delta;
+
+ // last packet handling
+ if (xfer_delta < pipe_status->max_packet_size) {
+ pipe_status->xfer_remaining = 0;
+ }
+
+ // transfer complete
+ xfer_complete = (xfer_result != XFER_RESULT_SUCCESS) || (pipe_status->xfer_remaining == 0);
+ if (xfer_complete) {
+ return true;
+ }
+
+ // continue receiving
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // continue sending
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // advance packet buffer
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg += xfer_delta;
+
+ // start next transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// Interrupt Handler
+void hcd_int_handler(uint8_t rhport, bool in_isr)
+{
+ (void) rhport;
+
+ uint16_t int_flags;
+ uint8_t pint_flags;
+ xfer_result_t xfer_result;
+ volatile usb_pipe_status_t* pipe_status;
+
+ //
+ // Check INTFLAG
+ //
+ int_flags = USB->HOST.INTFLAG.reg;
+ if (int_flags & USB_HOST_INTFLAG_HSOF) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_HSOF;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RST) {
+ TU_LOG2("USB_HOST_INTFLAG_RST\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ }
+ if (int_flags & USB_HOST_INTFLAG_WAKEUP) {
+ TU_LOG3("USB_HOST_INTFLAG_WAKEUP\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_WAKEUP;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DNRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_DNRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DNRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_UPRSM) {
+ TU_LOG3("USB_HOST_INTFLAG_UPRSM\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_UPRSM;
+ }
+ if (int_flags & USB_HOST_INTFLAG_RAMACER) {
+ TU_LOG1("USB_HOST_INTFLAG_RAMACER\r\n");
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RAMACER;
+ }
+ if (int_flags & USB_HOST_INTFLAG_DCONN) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DCONN;
+ hcd_event_device_attach(rhport, in_isr);
+ }
+ if (int_flags & USB_HOST_INTFLAG_DDISC) {
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DDISC;
+ hcd_event_device_remove(rhport, in_isr);
+ }
+
+ // handle pipe interrupts
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ // get pipe handle
+ pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr >= UINT8_MAX) {
+ continue;
+ }
+
+ //
+ // Check PINTFLAG
+ //
+ pint_flags = USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+ xfer_result = XFER_RESULT_INVALID;
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT0) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT0;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRCPT1) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRCPT1;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TXSTP) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TXSTP;
+ xfer_result = XFER_RESULT_SUCCESS;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_STALL) {
+ TU_LOG2("USB_HOST_PINTFLAG_STALL\r\n");
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_STALL;
+ xfer_result = XFER_RESULT_STALLED;
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_TRFAIL) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_TRFAIL;
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_ERRORFLOW) {
+ TU_LOG1("USB_HOST_STATUS_BK_ERRORFLOW\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else if (usb_pipe_table[pipe].HostDescBank[0].STATUS_BK.reg & USB_HOST_STATUS_BK_CRCERR) {
+ TU_LOG1("USB_HOST_STATUS_BK_CRCERR\r\n");
+ xfer_result = XFER_RESULT_FAILED;
+ } else {
+ // SAMD Quirk #1:
+ // Likes to report TRFAIL for no apparent reason -> ignore
+ }
+ }
+ if (pint_flags & USB_HOST_PINTFLAG_PERR) {
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg = USB_HOST_PINTFLAG_PERR;
+ // Handled by STATUS_PIPE checks below
+ }
+
+ //
+ // Check STATUS_PIPE
+ //
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DTGLER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DTGLER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DTGLER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_DAPIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_DAPIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_DAPIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_PIDER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_PIDER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_PIDER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_CRC16ER) {
+ TU_LOG1("USB_HOST_STATUS_PIPE_CRC16ER\r\n");
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_CRC16ER;
+ xfer_result = XFER_RESULT_FAILED;
+ }
+ if (usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg & USB_HOST_STATUS_PIPE_TOUTER) {
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.reg &= ~USB_HOST_STATUS_PIPE_TOUTER;
+
+ if ((USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE == USB_HOST_PTYPE_INT) &&
+ (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN)) {
+ // ignore timeouts from INT pipes
+ } else {
+ if (xfer_result == XFER_RESULT_INVALID) {
+ xfer_result = XFER_RESULT_TIMEOUT;
+ }
+ }
+ }
+
+ // prevent PERR from too high error counts, that is handled by TinyUSB anyways
+ usb_pipe_table[pipe].HostDescBank[0].STATUS_PIPE.bit.ERCNT = 0;
+
+ // no updates
+ if (xfer_result == XFER_RESULT_INVALID) {
+ continue;
+ }
+
+ // continue / complete transfer
+ if (samd_on_xfer(pipe, xfer_result)) {
+ hcd_event_xfer_complete(pipe_status->dev_addr, pipe_status->ep_addr, pipe_status->xfer_length, xfer_result, true);
+ }
+ }
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ TU_ASSERT(rhport == 0);
+ (void) rh_init;
+ fake_fnum = 0;
+
+ // reset to get in a clean state.
+ USB->HOST.CTRLA.bit.SWRST = 1;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 0)
+ ;
+ while (USB->HOST.SYNCBUSY.bit.SWRST == 1)
+ ;
+
+ // load pad calibration
+ USB->HOST.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos;
+ USB->HOST.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos;
+ USB->HOST.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos;
+
+ USB->HOST.QOSCTRL.bit.CQOS = 3; // High Quality
+ USB->HOST.QOSCTRL.bit.DQOS = 3; // High Quality
+
+ // configure host-mode
+ samd_free_all_pipes(); // initializes pipe handles and usb_pipe_table
+ USB->HOST.DESCADD.reg = (uint32_t) (&usb_pipe_table[0]);
+ USB->HOST.CTRLB.reg = USB_HOST_CTRLB_SPDCONF_NORMAL | USB_HOST_CTRLB_VBUSOK;
+ USB->HOST.CTRLA.reg = USB_CTRLA_MODE_HOST | USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
+ while (USB->HOST.SYNCBUSY.bit.ENABLE == 1)
+ ;
+
+ // enable basic USB interrupts
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.INTENCLR.reg = USB_HOST_INTENCLR_MASK;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DCONN;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_DDISC;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_WAKEUP;
+ USB->HOST.INTENSET.reg = USB_HOST_INTENSET_RST;
+
+ return true;
+}
+
+#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_0_IRQn);
+ NVIC_EnableIRQ(USB_1_IRQn);
+ NVIC_EnableIRQ(USB_2_IRQn);
+ NVIC_EnableIRQ(USB_3_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_3_IRQn);
+ NVIC_DisableIRQ(USB_2_IRQn);
+ NVIC_DisableIRQ(USB_1_IRQn);
+ NVIC_DisableIRQ(USB_0_IRQn);
+}
+
+#elif CFG_TUSB_MCU == OPT_MCU_SAMD11 || CFG_TUSB_MCU == OPT_MCU_SAMD21 || \
+ CFG_TUSB_MCU == OPT_MCU_SAML22 || CFG_TUSB_MCU == OPT_MCU_SAML21
+
+// Enable USB interrupt
+void hcd_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+ NVIC_DisableIRQ(USB_IRQn);
+}
+
+#else
+
+#error "No implementation available for hcd_int_enable / hcd_int_disable"
+
+#endif
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport)
+{
+ (void) rhport;
+
+// SAMD Quirk #2:
+// FNUM is stalled before enumeration of Low-Speed devices.
+// internal frame counter can be used as workaround (not very accurate)
+#ifdef HCD_SAMD_FAKE_FNUM
+ uint8_t start, current, prev;
+ uint8_t loop_count = (USB->HOST.STATUS.bit.SPEED == TUSB_SPEED_HIGH) ? 8 : 1;
+ for (uint8_t i = 0; i < loop_count; i++) {
+ start = USB->HOST.FLENHIGH.reg;
+ current = start;
+ // wait until wrap-around
+ prev = current;
+ while (current <= start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ // wait until start is reached again
+ prev = current;
+ while (current > start) {
+ current = USB->HOST.FLENHIGH.reg;
+ if (current > prev)
+ break;
+ prev = current;
+ }
+ }
+ fake_fnum += 1;
+ return fake_fnum;
+#else
+ return USB->HOST.FNUM.bit.FNUM;
+#endif // HCD_SAMD_FAKE_FNUM
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport)
+{
+ TU_ASSERT(rhport == 0);
+ return USB->HOST.STATUS.bit.LINESTATE != 0;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be
+// complete. Some port would require hcd_port_reset_end() to be invoked after 10ms to
+// complete the reset sequence.
+void hcd_port_reset(uint8_t rhport)
+{
+ hcd_int_disable(rhport);
+ samd_free_all_pipes();
+ USB->HOST.INTFLAG.reg |= USB->HOST.INTFLAG.reg; // clear pending
+ USB->HOST.CTRLB.bit.BUSRESET = 1;
+ fake_fnum = 0;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport)
+{
+ while (USB->HOST.INTFLAG.bit.RST == 0)
+ ;
+ USB->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
+ USB->HOST.CTRLB.bit.SOFE = 1;
+ hcd_int_enable(rhport);
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+ (void) rhport;
+
+ switch (USB->HOST.STATUS.bit.SPEED) {
+ case 0:
+ return TUSB_SPEED_FULL;
+ case 1:
+ return TUSB_SPEED_LOW;
+ case 2:
+ return TUSB_SPEED_HIGH;
+ default:
+ return TUSB_SPEED_INVALID;
+ }
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+ (void) rhport;
+
+ for (uint8_t pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ volatile usb_pipe_status_t* pipe_status = &usb_pipe_status_table[pipe];
+ if (pipe_status->dev_addr == dev_addr) {
+ samd_free_pipe(pipe);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+ const uint8_t ep_addr = ep_desc->bEndpointAddress;
+ const uint8_t bmAttributes = (ep_desc->bmAttributes.xfer) |
+ ((ep_desc->bmAttributes.sync) << 2) |
+ ((ep_desc->bmAttributes.usage) << 4);
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // initial configuration
+ pipe_status = &usb_pipe_status_table[pipe];
+ USB->HOST.HostPipe[pipe].PCFG.reg &= ~USB_HOST_PCFG_PTYPE_Msk;
+ USB->HOST.HostPipe[pipe].PCFG.bit.PTYPE = bmAttributes + 1;
+ USB->HOST.HostPipe[pipe].BINTERVAL.reg = ep_desc->bInterval;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+ USB->HOST.HostPipe[pipe].PINTENCLR.reg = USB_HOST_PINTENCLR_MASK;
+ pipe_status->max_packet_size = ep_desc->wMaxPacketSize;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.SIZE = USB_PCKSIZE_ENUM(pipe_status->max_packet_size);
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.AUTO_ZLP = 0;
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete
+// hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, 0);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) setup_packet;
+ pipe_status->xfer_remaining = 8;
+ pipe_status->xfer_length = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 8;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t* buffer, uint16_t buflen)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ // configure the pipe
+ pipe = samd_configure_pipe(dev_addr, ep_addr);
+ if (pipe >= USB_PIPE_NUM) {
+ return false;
+ }
+
+ // prepare transfer
+ pipe_status = &usb_pipe_status_table[pipe];
+ usb_pipe_table[pipe].HostDescBank[0].ADDR.reg = (uint32_t) buffer;
+ pipe_status->xfer_remaining = buflen;
+ pipe_status->xfer_length = 0;
+ // receive data
+ if (tu_edpt_dir(pipe_status->ep_addr) == TUSB_DIR_IN) {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = pipe_status->max_packet_size;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
+ }
+ // send data
+ else {
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
+ (pipe_status->xfer_remaining < pipe_status->max_packet_size) ? pipe_status->xfer_remaining
+ : pipe_status->max_packet_size;
+ usb_pipe_table[pipe].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
+ }
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // begin transfer
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_PFREEZE;
+
+ return true;
+}
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been
+// started Return true if a queued transfer is aborted, false if there is no transfer
+// to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_abort_xfer(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // no transfer in progress
+ if (USB->HOST.HostPipe[pipe].PSTATUS.bit.PFREEZE == 1) {
+ return false;
+ }
+
+ // abort the transfer
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ pipe_status = &usb_pipe_status_table[pipe];
+ pipe_status->xfer_length = 0;
+ pipe_status->xfer_remaining = 0;
+
+ return true;
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
+{
+ TU_ASSERT(rhport == 0);
+
+ uint8_t pipe;
+ volatile usb_pipe_status_t* pipe_status;
+
+ TU_LOG3("hcd_edpt_clear_stall(dev_addr=%02X, ep_addr=%02X)=", dev_addr, ep_addr);
+
+ // find the pipe
+ for (pipe = 0; pipe < USB_PIPE_NUM; pipe++) {
+ pipe_status = &usb_pipe_status_table[pipe];
+ if ((pipe_status->dev_addr == dev_addr) && (pipe_status->ep_addr == ep_addr)) {
+ break;
+ }
+ }
+
+ // pipe not found
+ if (pipe >= USB_PIPE_NUM) {
+ TU_LOG3("ERR_NO_PIPE\r\n");
+ return false;
+ }
+ TU_LOG3("%d\r\n", pipe);
+
+ // clear pending interrupts
+ USB->HOST.HostPipe[pipe].PINTFLAG.reg |= USB->HOST.HostPipe[pipe].PINTFLAG.reg;
+
+ // clear stalled state
+ USB->HOST.HostPipe[pipe].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
+ USB->HOST.HostPipe[pipe].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_DTGL;
+
+ return true;
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 52d675611..f10f0bdc3 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -39,6 +39,7 @@
#define DWC2_DEBUG 2
#include "device/dcd.h"
+#include "device/usbd_pvt.h"
#include "dwc2_common.h"
//--------------------------------------------------------------------+
@@ -52,6 +53,7 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
+// This variable is modified from ISR context, so it must be protected by critical section
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
@@ -321,6 +323,9 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
+// Since this function returns void, it is not possible to return a boolean success message
+// We must make sure that this function is not called when the EP is disabled
+// Must be called from critical section
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
@@ -420,8 +425,13 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
+#if CFG_TUSB_MCU == OPT_MCU_STM32N6
+ // No hardware detection of Vbus B-session is available on the STM32N6
+ dwc2->stm32_gccfg |= STM32_GCCFG_VBVALOVAL;
+#endif
+
// Enable required interrupts
- dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
// TX FIFO empty level for interrupt is complete empty
uint32_t gahbcfg = dwc2->gahbcfg;
@@ -531,6 +541,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+ usbd_spin_lock(false);
+
_dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
@@ -548,8 +560,9 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
-
dfifo_device_init(rhport); // re-init dfifo
+
+ usbd_spin_unlock(false);
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -567,21 +580,31 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpo
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
-
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
+ bool ret;
- // EP0 can only handle one packet
- if (epnum == 0) {
- _dcd_data.ep0_pending[dir] = total_bytes;
+ usbd_spin_lock(false);
+
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
+
+ // EP0 can only handle one packet
+ if (epnum == 0) {
+ _dcd_data.ep0_pending[dir] = total_bytes;
+ }
+
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
}
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_unlock(false);
- return true;
+ return ret;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
@@ -594,17 +617,27 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
-
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
+ bool ret;
- // Schedule packets to be sent within interrupt
- // TODO xfer fifo may only available for slave mode
- edpt_schedule_packets(rhport, epnum, dir);
+ usbd_spin_lock(false);
- return true;
+ if (xfer->max_size == 0) {
+ ret = false; // Endpoint is closed
+ } else {
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+
+ // Schedule packets to be sent within interrupt
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
+ ret = true;
+ }
+
+ usbd_spin_unlock(false);
+
+ return ret;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
@@ -631,6 +664,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
//--------------------------------------------------------------------
// 7.4.1 Initialization on USB Reset
+// Must be called from critical section
static void handle_bus_reset(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
const uint8_t ep_count = dwc2_ep_count(dwc2);
@@ -983,29 +1017,34 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
*/
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
-
const uint32_t gintmask = dwc2->gintmsk;
const uint32_t gintsts = dwc2->gintsts & gintmask;
if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
+
+ usbd_spin_lock(true);
handle_bus_reset(rhport);
+ usbd_spin_unlock(true);
}
if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
dwc2->gintsts = GINTSTS_ENUMDNE;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
handle_enum_done(rhport);
}
if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
+ dwc2->gintmsk &= ~GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1025,6 +1064,7 @@ void dcd_int_handler(uint8_t rhport) {
if(gintsts & GINTSTS_SOF) {
dwc2->gintsts = GINTSTS_SOF;
+ dwc2->gintmsk |= GINTMSK_USBSUSPM;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
// Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 3309760ff..49b8c54cb 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -55,8 +55,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
-{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
-{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
+ { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
+ { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
};
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
index dec021f59..595c89b75 100644
--- a/src/portable/synopsys/dwc2/dwc2_info.md
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -1,58 +1,58 @@
-| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5 HS | XMC4500 | GD32VF103 |
-|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:-------------|:-------------|:------------|
-| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
-| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
-| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
-| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
-| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
-| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
-| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
-| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
-| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
-| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
-| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
-| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
-| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
-| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
-| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
-| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
-| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
-| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
-| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
-| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
-| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
-| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
-| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
-| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
-| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
-| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
-| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
-| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 |
+|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------|
+| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 |
+| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 |
+| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W |
+| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 |
+| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 |
+| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP |
+| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only |
+| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub |
+| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a |
+| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a |
+| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 |
+| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 |
+| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 |
+| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 |
+| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 |
+| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 |
+| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
+| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
+| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
+| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
+| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 |
+| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 |
+| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
+| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit |
+| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
+| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
+| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 |
+| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
+| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
index 25edcf22d..3b1993cc5 100755
--- a/src/portable/synopsys/dwc2/dwc2_info.py
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -21,7 +21,7 @@ dwc2_reg_value = {
'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
- 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ 'ST U5A5/H7RS/N6 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
}
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index c11c1eb05..5f2b8419c 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -77,6 +77,17 @@ extern "C" {
#define EP_MAX_HS 9
#define EP_FIFO_SIZE_HS 4096
+#elif CFG_TUSB_MCU == OPT_MCU_STM32N6
+ #include "stm32n6xx.h"
+ #define EP_MAX_FS 9
+ #define EP_FIFO_SIZE_FS 4096
+
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+
+ #define USB_OTG_HS_PERIPH_BASE USB1_OTG_HS_BASE
+ #define OTG_HS_IRQn USB1_OTG_HS_IRQn
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
@@ -268,6 +279,77 @@ static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
}
}
+//------------- DCache -------------//
+#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
+
+typedef struct {
+ uintptr_t start;
+ uintptr_t end;
+} mem_region_t;
+
+// Can be used to define additional uncached regions
+#ifndef CFG_DWC2_MEM_UNCACHED_REGIONS
+#define CFG_DWC2_MEM_UNCACHED_REGIONS
+#endif
+
+static mem_region_t uncached_regions[] = {
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+#if CFG_TUSB_MCU == OPT_MCU_STM32H7
+ {.start = 0x20000000, .end = 0x2001FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ // DTCM (although USB DMA can't transfer to/from DTCM)
+ {.start = 0x20000000, .end = 0x2002FFFF},
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
+ // DTCM
+ {.start = 0x20000000, .end = 0x2000FFFF},
+#else
+#error "Cache maintenance is not supported yet"
+#endif
+ CFG_DWC2_MEM_UNCACHED_REGIONS
+};
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) {
+ size = (size & ~(CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT-1)) + CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT;
+ }
+ return size;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool is_cache_mem(uintptr_t addr) {
+ for (unsigned int i = 0; i < TU_ARRAY_SIZE(uncached_regions); i++) {
+ if (uncached_regions[i].start <= addr && addr <= uncached_regions[i].end) { return false; }
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (is_cache_mem(addr32)) {
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index 2de15068a..257fa2833 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -381,6 +381,10 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// force host mode and wait for mode switch
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
+#if CFG_TUSB_MCU == OPT_MCU_STM32N6
+ // No hardware detection of Vbus B-session is available on the STM32N6
+ dwc2->stm32_gccfg &= ~STM32_GCCFG_VBVALOVAL;
+#endif
while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
// configure fixed-allocated fifo scheme
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index f8bf3c889..4a208b9df 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -27,7 +27,7 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
+#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"
#include "device/dcd.h"
diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c
new file mode 100644
index 000000000..d176f40c5
--- /dev/null
+++ b/src/portable/wch/hcd_ch32_usbfs.c
@@ -0,0 +1,625 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Mitsumine Suzu (verylowfreq)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_WCH_USBFS) && defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "host/usbh_pvt.h"
+
+#include "bsp/board_api.h"
+
+#include "ch32v20x.h"
+#include "ch32v20x_usb.h"
+
+#define USBFS_RX_BUF_LEN 64
+#define USBFS_TX_BUF_LEN 64
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_RX_Buf[USBFS_RX_BUF_LEN];
+TU_ATTR_ALIGNED(4) static uint8_t USBFS_TX_Buf[USBFS_TX_BUF_LEN];
+
+#define USB_XFER_TIMEOUT_MILLIS 100
+// #define USB_INTERRUPT_XFER_TIMEOUT_MILLIS 1
+
+#define PANIC(...) \
+ do { \
+ printf("%s() L%d: ", __func__, __LINE__); \
+ printf("\r\n[PANIC] " __VA_ARGS__); \
+ while (true) {} \
+ } while (false)
+
+#define LOG_CH32_USBFSH(...) TU_LOG3(__VA_ARGS__)
+
+// Busywait for delay microseconds/nanoseconds
+// static void loopdelay(uint32_t count)
+// {
+// volatile uint32_t c = count / 3;
+// if (c == 0) { return; }
+// // while (c-- != 0);
+// asm volatile(
+// "1: \n" // loop label
+// " addi %0, %0, -1 \n" // c--
+// " bne %0, zero, 1b \n" // if (c != 0) goto loop
+// : "+r"(c) // c is input/output operand
+// );
+// }
+
+
+// Endpoint status
+typedef struct usb_edpt {
+ // Is this a valid struct
+ bool configured;
+
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ uint8_t max_packet_size;
+
+ uint8_t xfer_type;
+
+ // Data toggle (0 or not 0) for DATA0/1
+ uint8_t data_toggle;
+} usb_edpt_t;
+
+static usb_edpt_t usb_edpt_list[CFG_TUH_DEVICE_MAX * 6] = {};
+
+typedef struct usb_current_xfer_st {
+ bool is_busy;
+ uint8_t dev_addr;
+ uint8_t ep_addr;
+ // Xfer started time in millis for timeout
+ uint32_t start_ms;
+ uint8_t *buffer;
+ uint16_t bufferlen;
+ uint16_t xferred_len;
+} usb_current_xfer_t;
+
+static volatile usb_current_xfer_t usb_current_xfer_info = {};
+
+static usb_edpt_t *get_edpt_record(uint8_t dev_addr, uint8_t ep_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ usb_edpt_t *cur = &usb_edpt_list[i];
+ if (cur->configured && cur->dev_addr == dev_addr && cur->ep_addr == ep_addr) {
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *get_empty_record_slot(void) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (!usb_edpt_list[i].configured) {
+ return &usb_edpt_list[i];
+ }
+ }
+ return NULL;
+}
+
+static usb_edpt_t *add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *slot = get_empty_record_slot();
+ if (slot == NULL) {
+ PANIC("add_edpt_record(0x%02x, 0x%02x, ...) no slot for new record\r\n", dev_addr, ep_addr);
+ }
+ TU_ASSERT(slot != NULL, NULL);
+
+ slot->dev_addr = dev_addr;
+ slot->ep_addr = ep_addr;
+ slot->max_packet_size = max_packet_size;
+ slot->xfer_type = xfer_type;
+ slot->data_toggle = 0;
+
+ slot->configured = true;
+
+ return slot;
+}
+
+static usb_edpt_t *get_or_add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) {
+ usb_edpt_t *ret = get_edpt_record(dev_addr, ep_addr);
+ if (ret != NULL) {
+ return ret;
+ } else {
+ return add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type);
+ }
+}
+
+static void remove_edpt_record_for_device(uint8_t dev_addr) {
+ for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+ if (usb_edpt_list[i].configured && usb_edpt_list[i].dev_addr == dev_addr) {
+ usb_edpt_list[i].configured = false;
+ }
+ }
+}
+
+// static void dump_edpt_record_list() {
+// for (size_t i = 0; i < TU_ARRAY_SIZE(usb_edpt_list); i++) {
+// usb_edpt_t* cur = &usb_edpt_list[i];
+// if (cur->configured) {
+// printf("[%2d] Device 0x%02x Endpoint 0x%02x\r\n", i, cur->dev_addr, cur->ep_addr);
+// } else {
+// printf("[%2d] not configured\r\n", i);
+// }
+// }
+// }
+
+static bool interrupt_enabled = false;
+
+/** Enable or disable USBFS Host function */
+static void hardware_init_host(bool enabled) {
+ // Reset USBOTG module
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
+
+ tusb_time_delay_ms_api(1);
+ USBOTG_H_FS->BASE_CTRL = 0;
+
+ if (!enabled) {
+ // Disable all feature
+ USBOTG_H_FS->BASE_CTRL = 0;
+ } else {
+ // Enable USB Host features
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(0);
+ USBOTG_H_FS->BASE_CTRL = USBFS_UC_HOST_MODE | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
+ USBOTG_H_FS->HOST_EP_MOD = USBFS_UH_EP_TX_EN | USBFS_UH_EP_RX_EN;
+ USBOTG_H_FS->HOST_RX_DMA = (uint32_t) USBFS_RX_Buf;
+ USBOTG_H_FS->HOST_TX_DMA = (uint32_t) USBFS_TX_Buf;
+ // USBOTG_H_FS->INT_EN = USBFS_UIE_TRANSFER | USBFS_UIE_DETECT;
+ USBOTG_H_FS->INT_EN = USBFS_UIE_DETECT;
+ }
+}
+
+static bool hardware_start_xfer(uint8_t pid, uint8_t ep_addr, uint8_t data_toggle) {
+ LOG_CH32_USBFSH("hardware_start_xfer(pid=%s(0x%02x), ep_addr=0x%02x, toggle=%d)\r\n",
+ pid == USB_PID_IN ? "IN" : pid == USB_PID_OUT ? "OUT"
+ : pid == USB_PID_SETUP ? "SETUP"
+ : "(other)",
+ pid, ep_addr, data_toggle);
+
+ // if (pid == USB_PID_IN)
+ // { // FIXME: long delay needed (at release build) about 30msec
+ // loopdelay(SystemCoreClock / 1000 * 30);
+ // }
+
+ uint8_t pid_edpt = (pid << 4) | (tu_edpt_number(ep_addr) & 0x0f);
+ USBOTG_H_FS->HOST_TX_CTRL = (data_toggle != 0) ? USBFS_UH_T_TOG : 0;
+ USBOTG_H_FS->HOST_RX_CTRL = (data_toggle != 0) ? USBFS_UH_R_TOG : 0;
+ USBOTG_H_FS->HOST_EP_PID = pid_edpt;
+ USBOTG_H_FS->INT_EN |= USBFS_UIE_TRANSFER;
+ USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ return true;
+}
+
+
+/** Set device address to communicate */
+static void hardware_update_device_address(uint8_t dev_addr) {
+ // Keep the bit of GP_BIT. Other 7bits are actual device address.
+ USBOTG_H_FS->DEV_ADDR = (USBOTG_H_FS->DEV_ADDR & USBFS_UDA_GP_BIT) | (dev_addr & USBFS_USB_ADDR_MASK);
+}
+
+/** Set port speed */
+static void hardware_update_port_speed(tusb_speed_t speed) {
+ LOG_CH32_USBFSH("hardware_update_port_speed(%s)\r\n", speed == TUSB_SPEED_FULL ? "Full" : speed == TUSB_SPEED_LOW ? "Low"
+ : "(invalid)");
+ switch (speed) {
+ case TUSB_SPEED_LOW:
+ USBOTG_H_FS->BASE_CTRL |= USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_PRE_PID_EN;
+ return;
+ case TUSB_SPEED_FULL:
+ USBOTG_H_FS->BASE_CTRL &= ~USBFS_UC_LOW_SPEED;
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ USBOTG_H_FS->HOST_SETUP &= ~USBFS_UH_PRE_PID_EN;
+ return;
+ default:
+ PANIC("hardware_update_port_speed(%d)\r\n", speed);
+ }
+}
+
+static void hardware_set_port_address_speed(uint8_t dev_addr) {
+ hardware_update_device_address(dev_addr);
+ tusb_speed_t rhport_speed = hcd_port_speed_get(0);
+ tusb_speed_t dev_speed = tuh_speed_get(dev_addr);
+ hardware_update_port_speed(dev_speed);
+ if (rhport_speed == TUSB_SPEED_FULL && dev_speed == TUSB_SPEED_LOW) {
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_LOW_SPEED;
+ }
+}
+
+static bool hardware_device_attached(void) {
+ return USBOTG_H_FS->MIS_ST & USBFS_UMS_DEV_ATTACH;
+}
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
+ (void) rhport;
+ (void) rh_init;
+ hardware_init_host(true);
+
+ return true;
+}
+
+bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+ hardware_init_host(false);
+
+ return true;
+}
+
+static bool int_state_for_portreset = false;
+
+void hcd_port_reset(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset()\r\n");
+ int_state_for_portreset = interrupt_enabled;
+ // NVIC_DisableIRQ(USBFS_IRQn);
+ hcd_int_disable(rhport);
+ hardware_update_device_address(0x00);
+
+ // USBOTG_H_FS->HOST_SETUP = 0x00;
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_BUS_RESET;
+
+ return;
+}
+
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_port_reset_end()\r\n");
+
+ USBOTG_H_FS->HOST_CTRL &= ~USBFS_UH_BUS_RESET;
+ tusb_time_delay_ms_api(2);
+
+ if ((USBOTG_H_FS->HOST_CTRL & USBFS_UH_PORT_EN) == 0) {
+ if (hcd_port_speed_get(0) == TUSB_SPEED_LOW) {
+ hardware_update_port_speed(TUSB_SPEED_LOW);
+ }
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ // Suppress the attached event
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_DETECT;
+
+ if (int_state_for_portreset) {
+ hcd_int_enable(rhport);
+ }
+}
+
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+
+ return hardware_device_attached();
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+ if (USBOTG_H_FS->MIS_ST & USBFS_UMS_DM_LEVEL) {
+ return TUSB_SPEED_LOW;
+ } else {
+ return TUSB_SPEED_FULL;
+ }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ LOG_CH32_USBFSH("hcd_device_close(%d, 0x%02x)\r\n", rhport, dev_addr);
+ remove_edpt_record_for_device(dev_addr);
+}
+
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+
+ return board_millis();
+}
+
+void hcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBFS_IRQn);
+ interrupt_enabled = true;
+}
+
+void hcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBFS_IRQn);
+ interrupt_enabled = false;
+}
+
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) rhport;
+ (void) in_isr;
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_DETECT) {
+ // Clear the flag
+ USBOTG_H_FS->INT_FG = USBFS_UIF_DETECT;
+ // Read the detection state
+ bool attached = hardware_device_attached();
+ LOG_CH32_USBFSH("hcd_int_handler() attached = %d\r\n", attached ? 1 : 0);
+ if (attached) {
+ hcd_event_device_attach(rhport, true);
+ } else {
+ hcd_event_device_remove(rhport, true);
+ }
+ return;
+ }
+
+ if (USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) {
+ // Disable transfer interrupt
+ USBOTG_H_FS->INT_EN &= ~USBFS_UIE_TRANSFER;
+ // Clear the flag
+ // USBOTG_H_FS->INT_FG = USBFS_UIF_TRANSFER;
+ // Copy PID and Endpoint
+ uint8_t pid_edpt = USBOTG_H_FS->HOST_EP_PID;
+ uint8_t status = USBOTG_H_FS->INT_ST;
+ uint8_t dev_addr = USBOTG_H_FS->DEV_ADDR & USBFS_USB_ADDR_MASK;
+ // Clear register to stop transfer
+ // USBOTG_H_FS->HOST_EP_PID = 0x00;
+
+ LOG_CH32_USBFSH("hcd_int_handler() pid_edpt=0x%02x\r\n", pid_edpt);
+
+ uint8_t request_pid = pid_edpt >> 4;
+ uint8_t response_pid = status & USBFS_UIS_H_RES_MASK;
+ uint8_t ep_addr = pid_edpt & 0x0f;
+ if (request_pid == USB_PID_IN) {
+ ep_addr |= 0x80;
+ }
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info == NULL) {
+ PANIC("\r\nget_edpt_record(0x%02x, 0x%02x) returned NULL in USBHD_IRQHandler\r\n", dev_addr, ep_addr);
+ }
+
+ if (status & USBFS_UIS_TOG_OK) {
+ edpt_info->data_toggle ^= 0x01;
+
+ switch (request_pid) {
+ case USB_PID_SETUP:
+ case USB_PID_OUT: {
+ uint16_t tx_len = USBOTG_H_FS->HOST_TX_LEN;
+ usb_current_xfer_info.bufferlen -= tx_len;
+ usb_current_xfer_info.xferred_len += tx_len;
+ if (usb_current_xfer_info.bufferlen == 0) {
+ LOG_CH32_USBFSH("USB_PID_%s completed %d bytes\r\n", request_pid == USB_PID_OUT ? "OUT" : "SETUP", usb_current_xfer_info.xferred_len);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, usb_current_xfer_info.xferred_len, XFER_RESULT_SUCCESS, true);
+ return;
+ } else {
+ LOG_CH32_USBFSH("USB_PID_OUT continue...\r\n");
+ usb_current_xfer_info.buffer += tx_len;
+ uint16_t copylen = USBFS_TX_BUF_LEN;
+ if (copylen > usb_current_xfer_info.bufferlen) {
+ copylen = usb_current_xfer_info.bufferlen;
+ }
+ memcpy(USBFS_TX_Buf, usb_current_xfer_info.buffer, copylen);
+ hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ case USB_PID_IN: {
+ uint16_t received_len = USBOTG_H_FS->RX_LEN;
+ usb_current_xfer_info.xferred_len += received_len;
+ uint16_t xferred_len = usb_current_xfer_info.xferred_len;
+ LOG_CH32_USBFSH("Read %d bytes\r\n", received_len);
+ // if (received_len > 0 && (usb_current_xfer_info.buffer == NULL || usb_current_xfer_info.bufferlen == 0)) {
+ // PANIC("Data received but buffer not set\r\n");
+ // }
+ memcpy(usb_current_xfer_info.buffer, USBFS_RX_Buf, received_len);
+ usb_current_xfer_info.buffer += received_len;
+ if ((received_len < edpt_info->max_packet_size) || (xferred_len == usb_current_xfer_info.bufferlen)) {
+ // USB device sent all data.
+ LOG_CH32_USBFSH("USB_PID_IN completed\r\n");
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, XFER_RESULT_SUCCESS, true);
+ return;
+ } else {
+ // USB device may send more data.
+ LOG_CH32_USBFSH("Read more data\r\n");
+ hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ return;
+ }
+ }
+ default: {
+ PANIC("Unknown PID: 0x%02x\n", request_pid);
+ }
+ }
+ } else {
+ if (response_pid == USB_PID_STALL) {
+ LOG_CH32_USBFSH("STALL response\r\n");
+ hcd_edpt_clear_stall(0, dev_addr, ep_addr);
+ edpt_info->data_toggle = 0;
+ hardware_start_xfer(request_pid, ep_addr, 0);
+ return;
+ } else if (response_pid == USB_PID_NAK) {
+ LOG_CH32_USBFSH("NAK reposense\r\n");
+ uint32_t elapsed_time = board_millis() - usb_current_xfer_info.start_ms;
+ if (edpt_info->xfer_type == TUSB_XFER_INTERRUPT) {
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, true);
+ } else if (elapsed_time > USB_XFER_TIMEOUT_MILLIS) {
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true);
+ } else {
+ hardware_start_xfer(request_pid, ep_addr, edpt_info->data_toggle);
+ }
+ return;
+ } else if (response_pid == USB_PID_DATA0 || response_pid == USB_PID_DATA1) {
+ LOG_CH32_USBFSH("Data toggle mismatched and DATA0/1 (not STALL). RX_LEN=%d\r\n", USBOTG_H_FS->RX_LEN);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true);
+ return;
+ } else {
+ LOG_CH32_USBFSH("In USBHD_IRQHandler, unexpected response PID: 0x%02x\r\n", response_pid);
+ usb_current_xfer_info.is_busy = false;
+ hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true);
+ return;
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc) {
+ (void) rhport;
+ uint8_t ep_addr = ep_desc->bEndpointAddress;
+ uint8_t ep_num = tu_edpt_number(ep_addr);
+ uint16_t max_packet_size = ep_desc->wMaxPacketSize;
+ uint8_t xfer_type = ep_desc->bmAttributes.xfer;
+ LOG_CH32_USBFSH("hcd_edpt_open(rhport=%d, dev_addr=0x%02x, %p) EndpointAdderss=0x%02x,maxPacketSize=%d,xfer_type=%d\r\n", rhport, dev_addr, ep_desc, ep_addr, max_packet_size, xfer_type);
+
+ if (ep_num == 0x00) {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x00, max_packet_size, xfer_type) != NULL, false);
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x80, max_packet_size, xfer_type) != NULL, false);
+ } else {
+ TU_ASSERT(get_or_add_edpt_record(dev_addr, ep_addr, max_packet_size, xfer_type) != NULL, false);
+ }
+
+ USBOTG_H_FS->HOST_CTRL |= USBFS_UH_PORT_EN;
+ USBOTG_H_FS->HOST_SETUP |= USBFS_UH_SOF_EN;
+
+ hardware_set_port_address_speed(dev_addr);
+
+ return true;
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
+ (void) rhport;
+
+ LOG_CH32_USBFSH("hcd_edpt_xfer(%d, 0x%02x, 0x%02x, ...)\r\n", rhport, dev_addr, ep_addr);
+
+ while (usb_current_xfer_info.is_busy) {}
+ usb_current_xfer_info.is_busy = true;
+
+ usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr);
+ if (edpt_info == NULL) {
+ PANIC("get_edpt_record() returned NULL in hcd_edpt_xfer()\r\n");
+ }
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.buffer = buffer;
+ usb_current_xfer_info.bufferlen = buflen;
+ usb_current_xfer_info.start_ms = board_millis();
+ usb_current_xfer_info.xferred_len = 0;
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): READ, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ return hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle);
+ } else {
+ LOG_CH32_USBFSH("hcd_edpt_xfer(): WRITE, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen);
+ uint16_t copylen = USBFS_TX_BUF_LEN;
+ if (copylen > buflen) {
+ copylen = buflen;
+ }
+ USBOTG_H_FS->HOST_TX_LEN = copylen;
+ memcpy(USBFS_TX_Buf, buffer, copylen);
+ return hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle);
+ }
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+
+ while (usb_current_xfer_info.is_busy) {}
+
+ usb_current_xfer_info.is_busy = true;
+
+ LOG_CH32_USBFSH("hcd_setup_send(rhport=%d, dev_addr=0x%02x, %p)\r\n", rhport, dev_addr, setup_packet);
+
+ hardware_set_port_address_speed(dev_addr);
+
+ usb_edpt_t *edpt_info_tx = get_edpt_record(dev_addr, 0x00);
+ usb_edpt_t *edpt_info_rx = get_edpt_record(dev_addr, 0x80);
+ TU_ASSERT(edpt_info_tx != NULL, false);
+ TU_ASSERT(edpt_info_rx != NULL, false);
+
+ // Initialize data toggle (SETUP always starts with DATA0)
+ // Data toggle for OUT is toggled in hcd_int_handler()
+ edpt_info_tx->data_toggle = 0;
+ // Data toggle for IN must be set 0x01 manually.
+ edpt_info_rx->data_toggle = 0x01;
+ const uint16_t setup_packet_datalen = 8;
+ memcpy(USBFS_TX_Buf, setup_packet, setup_packet_datalen);
+ USBOTG_H_FS->HOST_TX_LEN = setup_packet_datalen;
+ uint8_t ep_addr = (setup_packet[0] & 0x80) ? 0x80 : 0x00;
+ usb_current_xfer_info.dev_addr = dev_addr;
+ usb_current_xfer_info.ep_addr = ep_addr;
+ usb_current_xfer_info.start_ms = board_millis();
+ usb_current_xfer_info.buffer = USBFS_TX_Buf;
+ usb_current_xfer_info.bufferlen = setup_packet_datalen;
+ usb_current_xfer_info.xferred_len = 0;
+
+ hardware_start_xfer(USB_PID_SETUP, 0, 0);
+
+ return true;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall(rhport=%d, dev_addr=0x%02x, ep_addr=0x%02x)\r\n", rhport, dev_addr, ep_addr);
+ // PANIC("\r\install\r\n");
+ uint8_t edpt_num = tu_edpt_number(ep_addr);
+ uint8_t setup_request_clear_stall[8] = {
+ 0x02, 0x01, 0x00, 0x00, edpt_num, 0x00, 0x00, 0x00
+ };
+ memcpy(USBFS_TX_Buf, setup_request_clear_stall, 8);
+ USBOTG_H_FS->HOST_TX_LEN = 8;
+
+ bool prev_int_state = interrupt_enabled;
+ hcd_int_disable(0);
+
+ USBOTG_H_FS->HOST_EP_PID = (USB_PID_SETUP << 4) | 0x00;
+ USBOTG_H_FS->INT_FG |= USBFS_UIF_TRANSFER;
+ while ((USBOTG_H_FS->INT_FG & USBFS_UIF_TRANSFER) == 0) {}
+ USBOTG_H_FS->HOST_EP_PID = 0;
+ uint8_t response_pid = USBOTG_H_FS->INT_ST & USBFS_UIS_H_RES_MASK;
+ (void) response_pid;
+ LOG_CH32_USBFSH("hcd_edpt_clear_stall() response pid=0x%02x\r\n", response_pid);
+
+ if (prev_int_state) {
+ hcd_int_enable(0);
+ }
+
+ return true;
+}
+
+#endif