diff options
| author | HiFiPhile <[email protected]> | 2025-07-11 15:47:55 +0200 |
|---|---|---|
| committer | HiFiPhile <[email protected]> | 2025-07-11 15:47:55 +0200 |
| commit | f074815405f9c510f3f1eb57f3cbdab40e9e1caa (patch) | |
| tree | 27157de2ae8ef662974a9d87a8119c12f3fbdffb /src/portable | |
| parent | 69f6b57772ad4f2923dcf48aabaaba044085c4b0 (diff) | |
| parent | 29c2af7651f9075a63a036e94c7bbe3d0d00c31a (diff) | |
Merge branch 'master' into feature/usbtmc_vendor-specific
Diffstat (limited to 'src/portable')
| -rw-r--r-- | src/portable/analog/max3421/hcd_max3421.c | 69 | ||||
| -rw-r--r-- | src/portable/chipidea/ci_hs/dcd_ci_hs.c | 4 | ||||
| -rw-r--r-- | src/portable/microchip/samd/hcd_samd.c | 765 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/dcd_dwc2.c | 82 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/dwc2_esp32.h | 4 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/dwc2_info.md | 116 | ||||
| -rwxr-xr-x | src/portable/synopsys/dwc2/dwc2_info.py | 2 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/dwc2_stm32.h | 82 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/hcd_dwc2.c | 4 | ||||
| -rw-r--r-- | src/portable/wch/dcd_ch32_usbhs.c | 2 | ||||
| -rw-r--r-- | src/portable/wch/hcd_ch32_usbfs.c | 625 |
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 |
