diff options
| author | HiFiPhile <[email protected]> | 2024-08-02 11:52:35 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-08-02 11:52:35 +0200 |
| commit | 95cb319bded7db536edaae24936a825a8524a4a2 (patch) | |
| tree | 7de7d7f8d1916061cdaefcfb4f220397e1d0fbb8 /src/portable | |
| parent | adc7a78fd6fbdccbe5f586391997052f2becd149 (diff) | |
| parent | 4232642899362fa5e9cf0dc59bad6f1f6d32c563 (diff) | |
Merge branch 'master' into vendor_fifo
Diffstat (limited to 'src/portable')
26 files changed, 2960 insertions, 2440 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c index 39afb7505..d9b8e1fc3 100644 --- a/src/portable/analog/max3421/hcd_max3421.c +++ b/src/portable/analog/max3421/hcd_max3421.c @@ -174,7 +174,8 @@ enum { enum { EP_STATE_IDLE = 0, EP_STATE_COMPLETE = 1, - EP_STATE_ATTEMPT_1 = 2, // pending 1st attempt + EP_STATE_ABORTING = 2, + EP_STATE_ATTEMPT_1 = 3, // pending 1st attempt EP_STATE_ATTEMPT_MAX = 15 }; @@ -459,6 +460,7 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param; _tuh_cfg = cfg->max3421; + _tuh_cfg.max_nak = tu_min8(_tuh_cfg.max_nak, EP_STATE_ATTEMPT_MAX-EP_STATE_ATTEMPT_1); return true; } @@ -479,13 +481,15 @@ bool hcd_init(uint8_t rhport) { _hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef); #endif + // NOTE: driver does not seem to work without nRST pin signal + // full duplex, interrupt negative edge reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false); // v1 is 0x01, v2 is 0x12, v3 is 0x13 uint8_t const revision = reg_read(rhport, REVISION_ADDR, false); - TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false); TU_LOG2_HEX(revision); + TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false); // reset reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false); @@ -676,7 +680,6 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool 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); - max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir); TU_VERIFY(ep); @@ -700,14 +703,19 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf 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) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; +bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr); + max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir); + TU_VERIFY(ep); - return false; + if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) { + hcd_int_disable(rhport); + ep->state = EP_STATE_ABORTING; + hcd_int_enable(rhport); + } + + return true; } // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked @@ -817,7 +825,6 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re static void handle_xfer_done(uint8_t rhport, bool in_isr) { uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr); uint8_t const hresult = hrsl & HRSL_RESULT_MASK; - uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK; uint8_t const hxfr_type = _hcd_data.hxfr & 0xf0; uint8_t const ep_dir = ((hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN)) ? 0 : 1; @@ -827,34 +834,30 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) { xfer_result_t xfer_result; switch(hresult) { - case HRSL_SUCCESS: - xfer_result = XFER_RESULT_SUCCESS; - break; - - case HRSL_STALL: - xfer_result = XFER_RESULT_STALLED; - break; - case HRSL_NAK: - if (ep_num == 0) { - // control endpoint -> retry immediately - hxfr_write(rhport, _hcd_data.hxfr, in_isr); + if (ep->state == EP_STATE_ABORTING) { + ep->state = EP_STATE_IDLE; } else { - if (ep->state < EP_STATE_ATTEMPT_MAX) { + if (ep_num == 0) { + // control endpoint -> retry immediately and return + hxfr_write(rhport, _hcd_data.hxfr, in_isr); + return; + } else if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) { ep->state++; } + } - max3421_ep_t * next_ep = find_next_pending_ep(ep); - if (ep == next_ep) { - // this endpoint is only one pending -> retry immediately - hxfr_write(rhport, _hcd_data.hxfr, in_isr); - } else if (next_ep) { - // switch to next pending endpoint TODO could have issue with double buffered if not clear previously out data - xact_generic(rhport, next_ep, true, in_isr); - } else { - // no more pending in this frame -> clear busy - atomic_flag_clear(&_hcd_data.busy); - } + max3421_ep_t * next_ep = find_next_pending_ep(ep); + if (ep == next_ep) { + // this endpoint is only one pending -> retry immediately + hxfr_write(rhport, _hcd_data.hxfr, in_isr); + } else if (next_ep) { + // switch to next pending endpoint + // TODO could have issue with double buffered if not clear previously out data + xact_generic(rhport, next_ep, true, in_isr); + } else { + // no more pending in this frame -> clear busy + atomic_flag_clear(&_hcd_data.busy); } return; @@ -862,6 +865,14 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) { // occurred when initialized without any pending transfer. Skip for now return; + case HRSL_SUCCESS: + xfer_result = XFER_RESULT_SUCCESS; + break; + + case HRSL_STALL: + xfer_result = XFER_RESULT_STALLED; + break; + default: TU_LOG3("HRSL: %02X\r\n", hrsl); xfer_result = XFER_RESULT_FAILED; @@ -940,9 +951,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (hirq & HIRQ_FRAME_IRQ) { _hcd_data.frame_count++; + // reset all endpoints nak counter, retry with 1st pending ep. max3421_ep_t* ep_retry = NULL; - - // reset all endpoints attempt counter for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) { max3421_ep_t* ep = &_hcd_data.ep[i]; if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) { @@ -1002,7 +1012,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ; - if ( hirq ) { + if (hirq) { hirq_write(rhport, hirq, 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 f9ec666e5..93e1d78dd 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -309,10 +309,12 @@ void dcd_disconnect(uint8_t rhport) void dcd_sof_enable(uint8_t rhport, bool en) { - (void) rhport; - (void) en; - - // TODO implement later + ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport); + if (en) { + dcd_reg->USBINTR |= INTR_SOF; + } else { + dcd_reg->USBINTR &= ~INTR_SOF; + } } //--------------------------------------------------------------------+ @@ -679,7 +681,8 @@ void dcd_int_handler(uint8_t rhport) if (int_status & INTR_SOF) { - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + const uint32_t frame = dcd_reg->FRINDEX; + dcd_event_sof(rhport, frame, true); } } diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index e145cbb1b..01bbf62bf 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -92,7 +92,7 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ -#if CFG_TUSB_DEBUG >= (EHCI_DBG + 1) +#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1) static inline void print_portsc(ehci_registers_t* regs) { TU_LOG_HEX(EHCI_DBG, regs->portsc); TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status); diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c index bfc0baa56..f912bef89 100644 --- a/src/portable/espressif/esp32sx/dcd_esp32sx.c +++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c @@ -31,7 +31,8 @@ #if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED) // Espressif -#include "xtensa_api.h" +#include "xtensa/xtensa_api.h" + #include "esp_intr_alloc.h" #include "esp_log.h" #include "soc/dport_reg.h" diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index 976d3dfd0..005b63faf 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/src/portable/microchip/samd/dcd_samd.c @@ -183,9 +183,12 @@ void dcd_connect(uint8_t rhport) void dcd_sof_enable(uint8_t rhport, bool en) { (void) rhport; - (void) en; - // TODO implement later + if (en) { + USB->DEVICE.INTENSET.bit.SOF = 1; + } else { + USB->DEVICE.INTENCLR.bit.SOF = 1; + } } /*------------------------------------------------------------------*/ @@ -374,7 +377,9 @@ void dcd_int_handler (uint8_t rhport) if ( int_status & USB_DEVICE_INTFLAG_SOF ) { USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF; - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + const uint32_t frame = USB->DEVICE.FNUM.bit.FNUM; + dcd_event_sof(0, frame, true); + //dcd_event_bus_signal(0, DCD_EVENT_SOF, true); } // SAMD doesn't distinguish between Suspend and Disconnect state. diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c index c3d0c7297..d5c0daaeb 100644 --- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c +++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c @@ -283,7 +283,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) /* Response with status first before changing device address */ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); } + +#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif + extern u32 SystemCoreClock; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + void dcd_remote_wakeup(uint8_t rhport) { (void) rhport; diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 80a285ef0..1cc5c1836 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -40,7 +40,6 @@ #include "nrf.h" #include "nrf_clock.h" -#include "nrf_power.h" #include "nrfx_usbd_errata.h" #ifdef __GNUC__ @@ -57,28 +56,46 @@ #include "mcu/mcu.h" #endif +/* Try to detect nrfx version if not configured with CFG_TUD_NRF_NRFX_VERSION + * nrfx v1 and v2 are concurrently developed. There is no NRFX_VERSION only MDK VERSION which is as follows: + * - v3.0.0: 8.53.1 (conflict with v2.11.0), v3.1.0: 8.55.0 ... + * - v2.11.0: 8.53.1, v2.6.0: 8.44.1, v2.5.0: 8.40.2, v2.4.0: 8.37.0, v2.3.0: 8.35.0, v2.2.0: 8.32.1, v2.1.0: 8.30.2, v2.0.0: 8.29.0 + * - v1.9.0: 8.40.3, v1.8.6: 8.35.0 (conflict with v2.3.0), v1.8.5: 8.32.3, v1.8.4: 8.32.1 (conflict with v2.2.0), + * v1.8.2: 8.32.1 (conflict with v2.2.0), v1.8.1: 8.27.1 + * Therefore the check for v1 would be: + * - MDK < 8.29.0 (v2.0), MDK == 8.32.3, 8.40.3 + * - in case of conflict User of those version must upgrade to other 1.x version or set CFG_TUD_NRF_NRFX_VERSION +*/ +#ifndef CFG_TUD_NRF_NRFX_VERSION + #define _MDK_VERSION (10000*MDK_MAJOR_VERSION + 100*MDK_MINOR_VERSION + MDK_MICRO_VERSION) + + #if _MDK_VERSION < 82900 || _MDK_VERSION == 83203 || _MDK_VERSION == 84003 + // nrfx <= 1.8.1, or 1.8.5 or 1.9.0 + #define CFG_TUD_NRF_NRFX_VERSION 1 + #else + #define CFG_TUD_NRF_NRFX_VERSION 2 + #endif +#endif + /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM *------------------------------------------------------------------*/ -enum -{ +enum { // Max allowed by USB specs - MAX_PACKET_SIZE = 64, + MAX_PACKET_SIZE = 64, // Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) | USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk }; -enum -{ - EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN +enum { + EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN EP_CBI_COUNT = 8 // Control Bulk Interrupt endpoints count }; // Transfer Descriptor -typedef struct -{ +typedef struct { uint8_t* buffer; uint16_t total_len; volatile uint16_t actual_len; @@ -96,113 +113,85 @@ typedef struct } xfer_td_t; // Data for managing dcd -static struct -{ +static struct { // All 8 endpoints including control IN & OUT (offset 1) // +1 for ISO endpoints xfer_td_t xfer[EP_CBI_COUNT + 1][2]; // nRF can only carry one DMA at a time, this is used to guard the access to EasyDMA - atomic_bool dma_running; -}_dcd; + atomic_flag dma_running; + + // Track whether sof has been manually enabled + bool sof_enabled; +} _dcd; /*------------------------------------------------------------------*/ /* Control / Bulk / Interrupt (CBI) Transfer *------------------------------------------------------------------*/ -// NVIC_GetEnableIRQ is only available in CMSIS v5 -#ifndef NVIC_GetEnableIRQ -static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn) -{ - if ((int32_t)(IRQn) >= 0) - { - return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); - } - else - { - return(0U); - } -} -#endif - // check if we are in ISR -TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) -{ +TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) { return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false; } // helper to start DMA -static void start_dma(volatile uint32_t* reg_startep) -{ +static void start_dma(volatile uint32_t* reg_startep) { (*reg_startep) = 1; - __ISB(); __DSB(); + __ISB(); + __DSB(); // TASKS_EP0STATUS, TASKS_EP0RCVOUT seem to need EasyDMA to be available // However these don't trigger any DMA transfer and got ENDED event subsequently // Therefore dma_pending is corrected right away - if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) ) - { + if ((reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT)) { atomic_flag_clear(&_dcd.dma_running); } } -static void edpt_dma_start(volatile uint32_t* reg_startep) -{ - if ( atomic_flag_test_and_set(&_dcd.dma_running) ) - { - usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true); - }else - { +static void edpt_dma_start(volatile uint32_t* reg_startep) { + if (atomic_flag_test_and_set(&_dcd.dma_running)) { + usbd_defer_func((osal_task_func_t)(uintptr_t ) edpt_dma_start, (void*) (uintptr_t) reg_startep, is_in_isr()); + } else { start_dma(reg_startep); } } // DMA is complete -static void edpt_dma_end(void) -{ - TU_ASSERT(_dcd.dma_running, ); +static void edpt_dma_end(void) { atomic_flag_clear(&_dcd.dma_running); } // helper getting td -static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) -{ +static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) { return &_dcd.xfer[epnum][dir]; } static void xact_out_dma(uint8_t epnum); + // Function wraps xact_out_dma which wants uint8_t while usbd_defer_func wants void (*)(void *) -static void xact_out_dma_wrapper(void *epnum) -{ - xact_out_dma((uint8_t)((uintptr_t)epnum)); +static void xact_out_dma_wrapper(void* epnum) { + xact_out_dma((uint8_t) ((uintptr_t) epnum)); } // Start DMA to move data from Endpoint -> RAM -static void xact_out_dma(uint8_t epnum) -{ +static void xact_out_dma(uint8_t epnum) { xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); uint32_t xact_len; // DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock, // If already running defer call regardless if it was called from ISR or task, - if ( atomic_flag_test_and_set(&_dcd.dma_running) ) - { - usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr()); + if (atomic_flag_test_and_set(&_dcd.dma_running)) { + usbd_defer_func((osal_task_func_t) xact_out_dma_wrapper, (void*) (uint32_t) epnum, is_in_isr()); return; } - if (epnum == EP_ISO_NUM) - { + if (epnum == EP_ISO_NUM) { xact_len = NRF_USBD->SIZE.ISOOUT; // If ZERO bit is set, ignore ISOOUT length - if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) - { + if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) { xact_len = 0; atomic_flag_clear(&_dcd.dma_running); - } - else - { - if (xfer->started) - { + } else { + if (xfer->started) { // Trigger DMA move data from Endpoint -> SRAM NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer; NRF_USBD->ISOOUT.MAXCNT = xact_len; @@ -212,9 +201,7 @@ static void xact_out_dma(uint8_t epnum) atomic_flag_clear(&_dcd.dma_running); } } - } - else - { + } else { // limit xact len to remaining length xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len); @@ -228,14 +215,13 @@ static void xact_out_dma(uint8_t epnum) // Prepare for a CBI transaction IN, call at the start // it start DMA to transfer data from RAM -> Endpoint -static void xact_in_dma(uint8_t epnum) -{ +static void xact_in_dma(uint8_t epnum) { xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); // Each transaction is up to Max Packet Size uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps); - NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer; + NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer; NRF_USBD->EPIN[epnum].MAXCNT = xact_len; edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]); @@ -244,26 +230,22 @@ static void xact_in_dma(uint8_t epnum) //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ -void dcd_init (uint8_t rhport) -{ +void dcd_init(uint8_t rhport) { TU_LOG2("dcd init\r\n"); (void) rhport; } -void dcd_int_enable(uint8_t rhport) -{ +void dcd_int_enable(uint8_t rhport) { (void) rhport; NVIC_EnableIRQ(USBD_IRQn); } -void dcd_int_disable(uint8_t rhport) -{ +void dcd_int_disable(uint8_t rhport) { (void) rhport; NVIC_DisableIRQ(USBD_IRQn); } -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void) rhport; (void) dev_addr; // Set Address is automatically update by hw controller, nothing to do @@ -278,8 +260,7 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr) NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk; } -void dcd_remote_wakeup(uint8_t rhport) -{ +void dcd_remote_wakeup(uint8_t rhport) { (void) rhport; // Bring controller out of low power mode @@ -288,8 +269,7 @@ void dcd_remote_wakeup(uint8_t rhport) } // disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(uint8_t rhport) -{ +void dcd_disconnect(uint8_t rhport) { (void) rhport; NRF_USBD->USBPULLUP = 0; @@ -299,56 +279,51 @@ void dcd_disconnect(uint8_t rhport) } // connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(uint8_t rhport) -{ +void dcd_connect(uint8_t rhport) { (void) rhport; NRF_USBD->USBPULLUP = 1; } -void dcd_sof_enable(uint8_t rhport, bool en) -{ +void dcd_sof_enable(uint8_t rhport, bool en) { (void) rhport; - (void) en; - - // TODO implement later + if (en) { + _dcd.sof_enabled = true; + NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk; + } else { + _dcd.sof_enabled = false; + NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; + } } //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { (void) rhport; uint8_t const ep_addr = desc_edpt->bEndpointAddress; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); _dcd.xfer[epnum][dir].mps = tu_edpt_packet_size(desc_edpt); - if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) - { - if (dir == TUSB_DIR_OUT) - { + if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) { + if (dir == TUSB_DIR_OUT) { NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum); NRF_USBD->EPOUTEN |= TU_BIT(epnum); // Write any value to SIZE register will allow nRF to ACK/accept data NRF_USBD->SIZE.EPOUT[epnum] = 0; - }else - { + } else { NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum); - NRF_USBD->EPINEN |= TU_BIT(epnum); + NRF_USBD->EPINEN |= TU_BIT(epnum); } // clear stall and reset DataToggle NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr; NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr; - } - else - { + } else { TU_ASSERT(epnum == EP_ISO_NUM); - if (dir == TUSB_DIR_OUT) - { + if (dir == TUSB_DIR_OUT) { // SPLIT ISO buffer when ISO IN endpoint is already opened. if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; @@ -361,9 +336,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint. NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk; NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk; - } - else - { + } else { NRF_USBD->EVENTS_ENDISOIN = 0; // SPLIT ISO buffer when ISO OUT endpoint is already opened. @@ -374,39 +347,38 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) // Enable SOF and ISOIN interrupts, and ISOIN endpoint. NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk; - NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk; + NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk; } } - __ISB(); __DSB(); + __ISB(); + __DSB(); return true; } -void dcd_edpt_close_all (uint8_t rhport) -{ +void dcd_edpt_close_all(uint8_t rhport) { // disable interrupt to prevent race condition dcd_int_disable(rhport); // disable all non-control (bulk + interrupt) endpoints - for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ ) - { + for (uint8_t ep = 1; ep < EP_CBI_COUNT; ep++) { NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep); NRF_USBD->TASKS_STARTEPIN[ep] = 0; NRF_USBD->TASKS_STARTEPOUT[ep] = 0; - tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t)); + tu_memclr(_dcd.xfer[ep], 2 * sizeof(xfer_td_t)); } // disable both ISO NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk; NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir; - NRF_USBD->TASKS_STARTISOIN = 0; + NRF_USBD->TASKS_STARTISOIN = 0; NRF_USBD->TASKS_STARTISOOUT = 0; - tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t)); + tu_memclr(_dcd.xfer[EP_ISO_NUM], 2 * sizeof(xfer_td_t)); // de-activate all non-control NRF_USBD->EPOUTEN = 1UL; @@ -415,107 +387,89 @@ void dcd_edpt_close_all (uint8_t rhport) dcd_int_enable(rhport); } -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - if (epnum != EP_ISO_NUM) - { + if (epnum != EP_ISO_NUM) { // CBI - if (dir == TUSB_DIR_OUT) - { + if (dir == TUSB_DIR_OUT) { NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum); NRF_USBD->EPOUTEN &= ~TU_BIT(epnum); - } - else - { + } else { NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum); NRF_USBD->EPINEN &= ~TU_BIT(epnum); } - } - else - { + } else { _dcd.xfer[EP_ISO_NUM][dir].mps = 0; // ISO - if (dir == TUSB_DIR_OUT) - { + if (dir == TUSB_DIR_OUT) { NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk; NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk; NRF_USBD->EVENTS_ENDISOOUT = 0; - } - else - { + } else { NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk; NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk; } // One of the ISO endpoints closed, no need to split buffers any more. NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir; // When both ISO endpoint are close there is no need for SOF any more. - if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; + if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) + NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; } _dcd.xfer[epnum][dir].started = false; - __ISB(); __DSB(); + __ISB(); + __DSB(); } -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); xfer_td_t* xfer = get_td(epnum, dir); TU_ASSERT(!xfer->started); - xfer->buffer = buffer; - xfer->total_len = total_bytes; + xfer->buffer = buffer; + xfer->total_len = total_bytes; xfer->actual_len = 0; // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE)); - if ( control_status ) - { + if (control_status) { // Status Phase also requires EasyDMA has to be available as well !!!! edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS); // The nRF doesn't interrupt on status transmit so we queue up a success response. dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr()); - } - else if ( dir == TUSB_DIR_OUT ) - { + } else if (dir == TUSB_DIR_OUT) { xfer->started = true; - if ( epnum == 0 ) - { + if (epnum == 0) { // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT); - }else - { + } else { // started just set, it could start DMA transfer if interrupt was trigger after this line // code only needs to start transfer (from Endpoint to RAM) when data_received was set // before started was set. If started is NOT set but data_received is, it means that // current transfer was already finished and next data is already present in endpoint and // can be consumed by future transfer - __ISB(); __DSB(); - if ( xfer->data_received && xfer->started ) - { + __ISB(); + __DSB(); + if (xfer->data_received && xfer->started) { // Data is already received previously // start DMA to copy to SRAM xfer->data_received = false; xact_out_dma(epnum); - } - else - { + } else { // nRF auto accept next Bulk/Interrupt OUT packet // nothing to do } } - } - else - { + } else { // Start DMA to copy data from RAM -> Endpoint xact_in_dma(epnum); } @@ -523,42 +477,37 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t return true; } -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); xfer_td_t* xfer = get_td(epnum, dir); - if ( epnum == 0 ) - { + if (epnum == 0) { NRF_USBD->TASKS_EP0STALL = 1; - }else if (epnum != EP_ISO_NUM) - { + } else if (epnum != EP_ISO_NUM) { NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr; // Note: nRF can auto ACK packet OUT before get stalled. // There maybe data in endpoint fifo already, we need to pull it out - if ( (dir == TUSB_DIR_OUT) && xfer->data_received ) - { + if ((dir == TUSB_DIR_OUT) && xfer->data_received) { xfer->data_received = false; xact_out_dma(epnum); } } - __ISB(); __DSB(); + __ISB(); + __DSB(); } -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void) rhport; uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - if ( epnum != 0 && epnum != EP_ISO_NUM ) - { + if (epnum != 0 && epnum != EP_ISO_NUM) { // reset data toggle to DATA0 // First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from // VALUE, or write it again with VALUE=Data0 or Data1 @@ -571,26 +520,25 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) // Write any value to SIZE register will allow nRF to ACK/accept data if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0; - __ISB(); __DSB(); + __ISB(); + __DSB(); } } /*------------------------------------------------------------------*/ /* Interrupt Handler *------------------------------------------------------------------*/ -void bus_reset(void) -{ +void bus_reset(void) { // 6.35.6 USB controller automatically disabled all endpoints (except control) NRF_USBD->EPOUTEN = 1UL; NRF_USBD->EPINEN = 1UL; - for(int i=0; i<8; i++) - { + for (int i = 0; i < 8; i++) { NRF_USBD->TASKS_STARTEPIN[i] = 0; NRF_USBD->TASKS_STARTEPOUT[i] = 0; } - NRF_USBD->TASKS_STARTISOIN = 0; + NRF_USBD->TASKS_STARTISOIN = 0; NRF_USBD->TASKS_STARTISOOUT = 0; // Clear USB Event Interrupt @@ -600,43 +548,40 @@ void bus_reset(void) // Reset interrupt NRF_USBD->INTENCLR = NRF_USBD->INTEN; NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk | - USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk; + USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | + USBD_INTEN_ENDEPOUT0_Msk; tu_varclr(&_dcd); _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE; _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE; } -void dcd_int_handler(uint8_t rhport) -{ +void dcd_int_handler(uint8_t rhport) { (void) rhport; - uint32_t const inten = NRF_USBD->INTEN; + uint32_t const inten = NRF_USBD->INTEN; uint32_t int_status = 0; volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET; - for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++) - { - if ( tu_bit_test(inten, i) && regevt[i] ) - { + for (uint8_t i = 0; i < USBD_INTEN_EPDATA_Pos + 1; i++) { + if (tu_bit_test(inten, i) && regevt[i]) { int_status |= TU_BIT(i); // event clear regevt[i] = 0; - __ISB(); __DSB(); + __ISB(); + __DSB(); } } - if ( int_status & USBD_INTEN_USBRESET_Msk ) - { + if (int_status & USBD_INTEN_USBRESET_Msk) { bus_reset(); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); } // ISOIN: Data was moved to endpoint buffer, client will be notified in SOF - if ( int_status & USBD_INTEN_ENDISOIN_Msk ) - { + if (int_status & USBD_INTEN_ENDISOIN_Msk) { xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN); xfer->actual_len = NRF_USBD->ISOIN.AMOUNT; @@ -645,13 +590,11 @@ void dcd_int_handler(uint8_t rhport) xfer->iso_in_transfer_ready = true; } - if ( int_status & USBD_INTEN_SOF_Msk ) - { + if (int_status & USBD_INTEN_SOF_Msk) { bool iso_enabled = false; // ISOOUT: Transfer data gathered in previous frame from buffer to RAM - if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) - { + if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) { iso_enabled = true; // Transfer from endpoint to RAM only if data is not corrupted if ((int_status & USBD_INTEN_USBEVENT_Msk) == 0 || @@ -661,38 +604,39 @@ void dcd_int_handler(uint8_t rhport) } // ISOIN: Notify client that data was transferred - if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) - { + if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) { iso_enabled = true; xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN); - if ( xfer->iso_in_transfer_ready ) - { + if (xfer->iso_in_transfer_ready) { xfer->iso_in_transfer_ready = false; dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); } } - if ( !iso_enabled ) - { - // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup - // so we disable it now - NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk; + if (!iso_enabled && !_dcd.sof_enabled) { + // SOF interrupt not manually enabled and ISO endpoint is not used, + // SOF is only enabled one-time for remote wakeup so we disable it now + + NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; } - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + const uint32_t frame = NRF_USBD->FRAMECNTR; + dcd_event_sof(0, frame, true); + //dcd_event_bus_signal(0, DCD_EVENT_SOF, true); } - if ( int_status & USBD_INTEN_USBEVENT_Msk ) - { - TU_LOG(3, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE); + if (int_status & USBD_INTEN_USBEVENT_Msk) { + TU_LOG(3, "EVENTCAUSE = 0x%04" PRIX32 "\r\n", NRF_USBD->EVENTCAUSE); - enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk | USBD_EVENTCAUSE_ISOOUTCRC_Msk }; + enum { + EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk | + USBD_EVENTCAUSE_ISOOUTCRC_Msk + }; uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK; NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt - if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk ) - { + if (evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk) { // Put controller into low power mode // Leave HFXO disable to application, since it may be used by other peripherals NRF_USBD->LOWPOWER = 1; @@ -700,8 +644,7 @@ void dcd_int_handler(uint8_t rhport) dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } - if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk ) - { + if (evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk) { // USB is out of low power mode, and wakeup is allowed // Initiate RESUME signal NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume; @@ -713,34 +656,29 @@ void dcd_int_handler(uint8_t rhport) NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk; } - if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk ) - { + if (evt_cause & USBD_EVENTCAUSE_RESUME_Msk) { dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); } } // Setup tokens are specific to the Control endpoint. - if ( int_status & USBD_INTEN_EP0SETUP_Msk ) - { - uint8_t const setup[8] = - { - NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH, - NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH + if (int_status & USBD_INTEN_EP0SETUP_Msk) { + uint8_t const setup[8] = { + NRF_USBD->BMREQUESTTYPE, NRF_USBD->BREQUEST, NRF_USBD->WVALUEL, NRF_USBD->WVALUEH, + NRF_USBD->WINDEXL, NRF_USBD->WINDEXH, NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH }; // nrf5x hw auto handle set address, there is no need to inform usb stack - tusb_control_request_t const * request = (tusb_control_request_t const *) setup; + tusb_control_request_t const* request = (tusb_control_request_t const*) setup; - if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient && - TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && - TUSB_REQ_SET_ADDRESS == request->bRequest) ) - { + if (!(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient && + TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && + TUSB_REQ_SET_ADDRESS == request->bRequest)) { dcd_event_setup_received(0, setup, true); } } - if ( int_status & EDPT_END_ALL_MASK ) - { + if (int_status & EDPT_END_ALL_MASK) { // DMA complete move data from SRAM <-> Endpoint // Must before endpoint transfer handling edpt_dma_end(); @@ -782,30 +720,24 @@ void dcd_int_handler(uint8_t rhport) * len if Host decides to sent fewer bytes, it this case transaction is also * complete and next transfer is not initiated here like for CBI. */ - for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++) - { - if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum)) - { + for (uint8_t epnum = 0; epnum < EP_CBI_COUNT + 1; epnum++) { + if (tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos + epnum)) { xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); uint16_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT; - xfer->buffer += xact_len; + xfer->buffer += xact_len; xfer->actual_len += xact_len; // Transfer complete if transaction len < Max Packet Size or total len is transferred - if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) ) - { - if ( epnum == 0 ) - { + if ((epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len)) { + if (epnum == 0) { // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT); - }else - { + } else { // nRF auto accept next Bulk/Interrupt OUT packet // nothing to do } - }else - { + } else { TU_ASSERT(xfer->started,); xfer->total_len = xfer->actual_len; xfer->started = false; @@ -819,11 +751,11 @@ void dcd_int_handler(uint8_t rhport) } // Endpoint <-> Host ( In & OUT ) - if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) ) - { + if (int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk)) { uint32_t data_status = NRF_USBD->EPDATASTATUS; NRF_USBD->EPDATASTATUS = data_status; - __ISB(); __DSB(); + __ISB(); + __DSB(); // EP0DATADONE is set with either Control Out on IN Data // Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN. @@ -832,22 +764,18 @@ void dcd_int_handler(uint8_t rhport) bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK); // CBI In: Endpoint -> Host (transaction complete) - for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) - { - if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) ) - { + for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) { + if (tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in)) { xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); uint8_t const xact_len = NRF_USBD->EPIN[epnum].AMOUNT; - xfer->buffer += xact_len; + xfer->buffer += xact_len; xfer->actual_len += xact_len; - if ( xfer->actual_len < xfer->total_len ) - { + if (xfer->actual_len < xfer->total_len) { // Start DMA to copy next data packet xact_in_dma(epnum); - } else - { + } else { // CBI IN complete dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); } @@ -855,17 +783,13 @@ void dcd_int_handler(uint8_t rhport) } // CBI OUT: Host -> Endpoint - for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) - { - if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) ) - { + for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) { + if (tu_bit_test(data_status, 16 + epnum) || (epnum == 0 && is_control_out)) { xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); - if ( xfer->started && xfer->actual_len < xfer->total_len ) - { + if (xfer->started && xfer->actual_len < xfer->total_len) { xact_out_dma(epnum); - }else - { + } else { // Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet // Mark this endpoint with data received xfer->data_received = true; @@ -889,76 +813,80 @@ void dcd_int_handler(uint8_t rhport) #define SD_MAGIC_NUMBER 0x51B1E5DB #endif -static inline bool is_sd_existed(void) -{ +TU_ATTR_ALWAYS_INLINE static inline bool is_sd_existed(void) { return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER; } // check if SD is existed and enabled -static inline bool is_sd_enabled(void) -{ +TU_ATTR_ALWAYS_INLINE static inline bool is_sd_enabled(void) { if ( !is_sd_existed() ) return false; - uint8_t sd_en = false; (void) sd_softdevice_is_enabled(&sd_en); return sd_en; } #endif -static bool hfclk_running(void) -{ +static bool hfclk_running(void) { #ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { + if ( is_sd_enabled() ) { uint32_t is_running = 0; (void) sd_clock_hfclk_is_running(&is_running); return (is_running ? true : false); } #endif +#if CFG_TUD_NRF_NRFX_VERSION == 1 + return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY); +#else return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY); +#endif } -static void hfclk_enable(void) -{ +static void hfclk_enable(void) { #if CFG_TUSB_OS == OPT_OS_MYNEWT usb_clock_request(); return; #else // already running, nothing to do - if ( hfclk_running() ) return; + if (hfclk_running()) return; #ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { + if ( is_sd_enabled() ) { (void)sd_clock_hfclk_request(); return; } #endif +#if CFG_TUD_NRF_NRFX_VERSION == 1 + nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED); + nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART); +#else nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); #endif +#endif } -static void hfclk_disable(void) -{ +static void hfclk_disable(void) { #if CFG_TUSB_OS == OPT_OS_MYNEWT usb_clock_release(); return; #else #ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { + if ( is_sd_enabled() ) { (void)sd_clock_hfclk_release(); return; } #endif +#if CFG_TUD_NRF_NRFX_VERSION == 1 + nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP); +#else nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); #endif +#endif } // Power & Clock Peripheral on nRF5x to manage USB @@ -971,8 +899,7 @@ static void hfclk_disable(void) // Therefore this function must be called to handle USB power event by // - nrfx_power_usbevt_init() : if Softdevice is not used or enabled // - SoftDevice SOC event : if SD is used and enabled -void tusb_hal_nrf_power_event (uint32_t event) -{ +void tusb_hal_nrf_power_event(uint32_t event) { // Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h enum { USB_EVT_DETECTED = 0, @@ -980,51 +907,42 @@ void tusb_hal_nrf_power_event (uint32_t event) USB_EVT_READY = 2 }; -#if CFG_TUSB_DEBUG >= 2 - const char* const power_evt_str[] = { "Detected", "Removed", "Ready" }; - TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]); +#if CFG_TUSB_DEBUG >= 3 + const char* const power_evt_str[] = {"Detected", "Removed", "Ready"}; + TU_LOG(3, "Power USB event: %s\r\n", power_evt_str[event]); #endif - switch ( event ) - { + switch (event) { case USB_EVT_DETECTED: - if ( !NRF_USBD->ENABLE ) - { + if (!NRF_USBD->ENABLE) { // Prepare for receiving READY event: disable interrupt since we will blocking wait NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk; NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; - __ISB(); __DSB(); // for sync + __ISB(); + __DSB(); // for sync #ifdef NRF52_SERIES // NRF53 does not need this errata // ERRATA 171, 187, 166 - if ( nrfx_usbd_errata_187() ) - { + if (nrfx_usbd_errata_187()) { // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006ED14)) = 0x00000003; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006ED14)) = 0x00000003; + if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) { + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + *((volatile uint32_t*) (0x4006ED14)) = 0x00000003; + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + } else { + *((volatile uint32_t*) (0x4006ED14)) = 0x00000003; } // CRITICAL_REGION_EXIT(); } - if ( nrfx_usbd_errata_171() ) - { + if (nrfx_usbd_errata_171()) { // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0; + if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) { + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0; + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + } else { + *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0; } // CRITICAL_REGION_EXIT(); } @@ -1032,64 +950,58 @@ void tusb_hal_nrf_power_event (uint32_t event) // Enable the peripheral (will cause Ready event) NRF_USBD->ENABLE = 1; - __ISB(); __DSB(); // for sync + __ISB(); + __DSB(); // for sync // Enable HFCLK hfclk_enable(); } - break; + break; case USB_EVT_READY: // Skip if pull-up is enabled and HCLK is already running. // Application probably call this more than necessary. - if ( NRF_USBD->USBPULLUP && hfclk_running() ) break; + if (NRF_USBD->USBPULLUP && hfclk_running()) break; // Waiting for USBD peripheral enabled - while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { } + while (!(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE)) {} NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; - __ISB(); __DSB(); // for sync + __ISB(); + __DSB(); // for sync #ifdef NRF52_SERIES - if ( nrfx_usbd_errata_171() ) - { + if (nrfx_usbd_errata_171()) { // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006EC14)) = 0x00000000; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006EC14)) = 0x00000000; + if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) { + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + *((volatile uint32_t*) (0x4006EC14)) = 0x00000000; + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + } else { + *((volatile uint32_t*) (0x4006EC14)) = 0x00000000; } // CRITICAL_REGION_EXIT(); } - if ( nrfx_usbd_errata_187() ) - { + if (nrfx_usbd_errata_187()) { // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006ED14)) = 0x00000000; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006ED14)) = 0x00000000; + if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) { + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + *((volatile uint32_t*) (0x4006ED14)) = 0x00000000; + *((volatile uint32_t*) (0x4006EC00)) = 0x00009375; + } else { + *((volatile uint32_t*) (0x4006ED14)) = 0x00000000; } // CRITICAL_REGION_EXIT(); } - if ( nrfx_usbd_errata_166() ) - { - *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3; - *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40; + if (nrfx_usbd_errata_166()) { + *((volatile uint32_t*) (NRF_USBD_BASE + 0x800)) = 0x7E3; + *((volatile uint32_t*) (NRF_USBD_BASE + 0x804)) = 0x40; - __ISB(); __DSB(); + __ISB(); + __DSB(); } #endif @@ -1101,30 +1013,31 @@ void tusb_hal_nrf_power_event (uint32_t event) // Enable interrupt, priorities should be set by application NVIC_ClearPendingIRQ(USBD_IRQn); + // Don't enable USBD interrupt yet, if dcd_init() did not finish yet // Interrupt will be enabled by tud_init(), when USB stack is ready // to handle interrupts. - if (tud_inited()) - { + if (tud_inited()) { NVIC_EnableIRQ(USBD_IRQn); } // Wait for HFCLK - while ( !hfclk_running() ) { } + while (!hfclk_running()) {} // Enable pull up NRF_USBD->USBPULLUP = 1; - __ISB(); __DSB(); // for sync - break; + __ISB(); + __DSB(); // for sync + break; case USB_EVT_REMOVED: - if ( NRF_USBD->ENABLE ) - { + if (NRF_USBD->ENABLE) { // Abort all transfers // Disable pull up NRF_USBD->USBPULLUP = 0; - __ISB(); __DSB(); // for sync + __ISB(); + __DSB(); // for sync // Disable Interrupt NVIC_DisableIRQ(USBD_IRQn); @@ -1133,15 +1046,17 @@ void tusb_hal_nrf_power_event (uint32_t event) NRF_USBD->INTENCLR = NRF_USBD->INTEN; NRF_USBD->ENABLE = 0; - __ISB(); __DSB(); // for sync + __ISB(); + __DSB(); // for sync hfclk_disable(); dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr()); } - break; + break; - default: break; + default: + break; } } diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c index dc71117b3..ef61146aa 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -540,7 +540,7 @@ void dcd_int_handler(uint8_t rhport) } if (is & USB_ISTAT_SLEEP_MASK) { - // TU_LOG2("Suspend: "); TU_LOG2_HEX(is); + // TU_LOG3("Suspend: "); TU_LOG2_HEX(is); // Note Host usually has extra delay after bus reset (without SOF), which could falsely // detected as Sleep event. Though usbd has debouncing logic so we are good diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c index f4ed09d83..cc18cf59b 100644 --- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c +++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c @@ -42,7 +42,18 @@ #if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX) // LPCOpen + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" + #pragma GCC diagnostic ignored "-Wstrict-prototypes" + #endif + #include "chip.h" + + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + #else // SDK #include "fsl_device_registers.h" @@ -239,7 +250,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_ return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type; } -TU_ATTR_ALWAYS_INLINE static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) { +TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) { return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0); } diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index 1ca711c77..43f48da39 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -53,6 +53,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) { //--------------------------------------------------------------------+ // Implementation //--------------------------------------------------------------------+ +// Provide own byte by byte memcpy as not all copies are aligned +static void unaligned_memcpy(void *dst, const void *src, size_t n) { + uint8_t *dst_byte = (uint8_t*)dst; + const uint8_t *src_byte = (const uint8_t*)src; + while (n--) { + *dst_byte++ = *src_byte++; + } +} void rp2040_usb_init(void) { // Reset usb controller @@ -67,7 +75,6 @@ void rp2040_usb_init(void) { #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif #endif - memset(usb_hw, 0, sizeof(*usb_hw)); memset(usb_dpram, 0, sizeof(*usb_dpram)); #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -125,7 +132,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, if (!ep->rx) { // Copy data from user buffer to hw buffer - memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); + unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); ep->user_buf += buflen; // Mark as full @@ -230,7 +237,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin // we have received AFTER we have copied it to the user buffer at the appropriate offset assert(buf_ctrl & USB_BUF_CTRL_FULL); - memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes); + unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes); ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); ep->user_buf += xferred_bytes; } diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index 24edc30e7..7c6044ca0 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -29,10 +29,6 @@ #if CFG_TUD_ENABLED && defined(TUP_USBIP_RUSB2) -// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) -// We disable SOF for now until needed later on -#define USE_SOF 0 - #include "device/dcd.h" #include "rusb2_type.h" @@ -57,30 +53,25 @@ //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM //--------------------------------------------------------------------+ +enum { + PIPE_COUNT = 10, +}; -/* Start of definition of packed structs (used by the CCRX toolchain) */ -TU_ATTR_PACKED_BEGIN -TU_ATTR_BIT_FIELD_ORDER_BEGIN - -typedef struct TU_ATTR_PACKED -{ +typedef struct { void *buf; /* the start address of a transfer data buffer */ uint16_t length; /* the number of bytes in the buffer */ uint16_t remaining; /* the number of bytes remaining in the buffer */ - struct { - uint32_t ep : 8; /* an assigned endpoint address */ - uint32_t ff : 1; /* `buf` is TU_FUFO or POD */ - uint32_t : 0; - }; -} pipe_state_t; -TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain) -TU_ATTR_BIT_FIELD_ORDER_END + uint8_t ep; /* an assigned endpoint address */ + uint8_t ff; /* `buf` is TU_FUFO or POD */ +} pipe_state_t; typedef struct { - pipe_state_t pipe[10]; + pipe_state_t pipe[PIPE_COUNT]; uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */ + // Track whether sof has been manually enabled + bool sof_enabled; } dcd_data_t; static dcd_data_t _dcd; @@ -89,52 +80,44 @@ static dcd_data_t _dcd; // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -// Transfer conditions specifiable for each pipe: -// - Pipe 0: Control transfer with 64-byte single buffer -// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up -// to 2 KB and optional double buffer -// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and -// optional double buffer -// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer -enum { - PIPE_1ST_BULK = 3, - PIPE_1ST_INTERRUPT = 6, - PIPE_COUNT = 10, -}; - -static unsigned find_pipe(unsigned xfer) -{ - switch (xfer) { - case TUSB_XFER_ISOCHRONOUS: - for (int i = 1; i < PIPE_1ST_BULK; ++i) { - if (0 == _dcd.pipe[i].ep) return i; - } - break; - case TUSB_XFER_BULK: - for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) { - if (0 == _dcd.pipe[i].ep) return i; - } - for (int i = 1; i < PIPE_1ST_BULK; ++i) { - if (0 == _dcd.pipe[i].ep) return i; - } - break; +// Transfer conditions specifiable for each pipe for most MCUs +// - Pipe 0: Control transfer with 64-byte single buffer +// - Pipes 1 and 2: Bulk or ISO +// - Pipes 3 to 5: Bulk +// - Pipes 6 to 9: Interrupt +// +// Note: for small mcu such as +// - RA2A1: only pipe 4-7 are available, and no support for ISO +static unsigned find_pipe(unsigned xfer_type) { + #if defined(BSP_MCU_GROUP_RA2A1) + const uint8_t pipe_idx_arr[4][2] = { + { 0, 0 }, // Control + { 0, 0 }, // Isochronous not supported + { 4, 5 }, // Bulk + { 6, 7 }, // Interrupt + }; + #else + const uint8_t pipe_idx_arr[4][2] = { + { 0, 0 }, // Control + { 1, 2 }, // Isochronous + { 1, 5 }, // Bulk + { 6, 9 }, // Interrupt + }; + #endif - case TUSB_XFER_INTERRUPT: - for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) { - if (0 == _dcd.pipe[i].ep) return i; - } - break; + // find backward since only pipe 1, 2 support ISO + const uint8_t idx_first = pipe_idx_arr[xfer_type][0]; + const uint8_t idx_last = pipe_idx_arr[xfer_type][1]; - default: - /* No support for control transfer */ - break; + for (int i = idx_last; i >= idx_first; i--) { + if (0 == _dcd.pipe[i].ep) return i; } + return 0; } -static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) -{ +static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) { if (num) { return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]); } else { @@ -142,8 +125,7 @@ static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) } } -static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) -{ +static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) { volatile reg_pipetre_t* tre = NULL; if ((1 <= num) && (num <= 5)) { tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E); @@ -151,8 +133,7 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) return tre; } -static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) -{ +static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) { rusb2_reg_t *rusb = RUSB2_REG(rhport); const unsigned epn = tu_edpt_number(ep_addr); @@ -165,19 +146,16 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) } } -static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) -{ +static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) { return rusb->DCPMAXP_b.MXPS; } -static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) -{ +static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) { rusb->PIPESEL = num; return rusb->PIPEMAXP; } -static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) -{ +static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) { while ( rusb->D0FIFOSEL_b.CURPIPE != num ) {} while ( !rusb->D0FIFOCTR_b.FRDY ) {} } @@ -266,14 +244,6 @@ static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void tu_fifo_advance_write_pointer(f, count); } - -static bool wait_pipe_fifo_empty(rusb2_reg_t* rusb, uint8_t num) { - TU_ASSERT(num); - while( (rusb->PIPE_CTR[num-1] & RUSB2_PIPE_CTR_INBUFM_Msk) > 0 ) {} - return true; -} - - //--------------------------------------------------------------------+ // Pipe Transfer //--------------------------------------------------------------------+ @@ -347,7 +317,6 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num) const unsigned rem = pipe->remaining; if (!rem) { - wait_pipe_fifo_empty(rusb, num); pipe->buf = NULL; return true; } @@ -693,6 +662,10 @@ void dcd_init(uint8_t rhport) rusb2_reg_t* rusb = RUSB2_REG(rhport); rusb2_module_start(rhport, true); +// We disable SOF for now until needed later on. +// Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval) +_dcd.sof_enabled = false; + #ifdef RUSB2_SUPPORT_HIGHSPEED if ( rusb2_is_highspeed_rhport(rhport) ) { rusb->SYSCFG_b.HSE = 1; @@ -737,7 +710,7 @@ void dcd_init(uint8_t rhport) rusb->INTSTS0 = 0; rusb->INTENB0 = RUSB2_INTSTS0_VBINT_Msk | RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_BEMP_Msk | - RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (USE_SOF ? RUSB2_INTSTS0_SOFR_Msk : 0) | + RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (_dcd.sof_enabled ? RUSB2_INTSTS0_SOFR_Msk : 0) | RUSB2_INTSTS0_RESM_Msk; rusb->BEMPENB = 1; rusb->BRDYENB = 1; @@ -785,10 +758,9 @@ void dcd_disconnect(uint8_t rhport) void dcd_sof_enable(uint8_t rhport, bool en) { - (void) rhport; - (void) en; - - // TODO implement later + rusb2_reg_t* rusb = RUSB2_REG(rhport); + _dcd.sof_enabled = en; + rusb->INTENB0_b.SOFE = en ? 1: 0; } //--------------------------------------------------------------------+ @@ -844,7 +816,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) } rusb->PIPECFG = cfg; - rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num); + rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num); rusb->BRDYENB |= TU_BIT(num); if (dir || (xfer != TUSB_XFER_BULK)) { @@ -944,6 +916,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) //--------------------------------------------------------------------+ // ISR //--------------------------------------------------------------------+ + +#if defined(__CCRX__) +TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) { + unsigned int count = 0; + while ((value & 1) == 0) { + value >>= 1; + count++; + } + return count; +} +#endif + void dcd_int_handler(uint8_t rhport) { rusb2_reg_t* rusb = RUSB2_REG(rhport); @@ -966,18 +950,19 @@ void dcd_int_handler(uint8_t rhport) // Resumed if ( is0 & RUSB2_INTSTS0_RESM_Msk ) { dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); -#if (0 == USE_SOF) - rusb->INTENB0_b.SOFE = 0; -#endif + if (!_dcd.sof_enabled) { + rusb->INTENB0_b.SOFE = 0; + } } // SOF received if ( (is0 & RUSB2_INTSTS0_SOFR_Msk) && rusb->INTENB0_b.SOFE ) { // USBD will exit suspended mode when SOF event is received - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); -#if (0 == USE_SOF) - rusb->INTENB0_b.SOFE = 0; -#endif + const uint32_t frame = rusb->FRMNUM_b.FRNM; + dcd_event_sof(rhport, frame, true); + if (!_dcd.sof_enabled) { + rusb->INTENB0_b.SOFE = 0; + } } // Device state changes @@ -996,9 +981,9 @@ void dcd_int_handler(uint8_t rhport) case RUSB2_INTSTS0_DVSQ_STATE_SUSP2: case RUSB2_INTSTS0_DVSQ_STATE_SUSP3: dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); -#if (0 == USE_SOF) - rusb->INTENB0_b.SOFE = 1; -#endif + if (!_dcd.sof_enabled) { + rusb->INTENB0_b.SOFE = 1; + } default: break; } @@ -1035,17 +1020,7 @@ void dcd_int_handler(uint8_t rhport) /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */ rusb->BRDYSTS = ~s; while (s) { -#if defined(__CCRX__) - static const int Mod37BitPosition[] = { - -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, - 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, - 20, 8, 19, 18 - }; - - const unsigned num = Mod37BitPosition[(-s & s) % 37]; -#else const unsigned num = __builtin_ctz(s); -#endif process_pipe_brdy(rhport, num); s &= ~TU_BIT(num); } diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c index bf95be707..f140da690 100644 --- a/src/portable/renesas/rusb2/hcd_rusb2.c +++ b/src/portable/renesas/rusb2/hcd_rusb2.c @@ -45,6 +45,9 @@ //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +enum { + PIPE_COUNT = 10, +}; TU_ATTR_PACKED_BEGIN TU_ATTR_BIT_FIELD_ORDER_BEGIN @@ -75,7 +78,7 @@ TU_ATTR_BIT_FIELD_ORDER_END typedef struct { bool need_reset; /* The device has not been reset after connection. */ - pipe_state_t pipe[10]; + pipe_state_t pipe[PIPE_COUNT]; uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */ uint8_t ctl_mps[5]; /* EP0 max packet size for each device */ } hcd_data_t; @@ -86,46 +89,30 @@ typedef struct static hcd_data_t _hcd; // TODO merged with DCD -// Transfer conditions specifiable for each pipe: +// Transfer conditions specifiable for each pipe for most MCUs // - Pipe 0: Control transfer with 64-byte single buffer -// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up -// to 2 KB and optional double buffer -// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and -// optional double buffer -// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer -enum { - PIPE_1ST_BULK = 3, - PIPE_1ST_INTERRUPT = 6, - PIPE_COUNT = 10, -}; - -static unsigned find_pipe(unsigned xfer) { - switch ( xfer ) { - case TUSB_XFER_ISOCHRONOUS: - for (int i = 1; i < PIPE_1ST_BULK; ++i) { - if ( 0 == _hcd.pipe[i].ep ) return i; - } - break; - - case TUSB_XFER_BULK: - for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) { - if ( 0 == _hcd.pipe[i].ep ) return i; - } - for (int i = 1; i < PIPE_1ST_BULK; ++i) { - if ( 0 == _hcd.pipe[i].ep ) return i; - } - break; +// - Pipes 1 and 2: Bulk or ISO +// - Pipes 3 to 5: Bulk +// - Pipes 6 to 9: Interrupt +// +// Note: for small mcu such as +// - RA2A1: only pipe 4-7 are available, and no support for ISO +static unsigned find_pipe(unsigned xfer_type) { + const uint8_t pipe_idx_arr[4][2] = { + { 0, 0 }, // Control + { 1, 2 }, // Isochronous + { 1, 5 }, // Bulk + { 6, 9 }, // Interrupt + }; - case TUSB_XFER_INTERRUPT: - for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) { - if ( 0 == _hcd.pipe[i].ep ) return i; - } - break; + // find backward since only pipe 1, 2 support ISO + const uint8_t idx_first = pipe_idx_arr[xfer_type][0]; + const uint8_t idx_last = pipe_idx_arr[xfer_type][1]; - default: - /* No support for control transfer */ - break; + for (int i = idx_last; i >= idx_first; i--) { + if (0 == _hcd.pipe[i].ep) return i; } + return 0; } @@ -718,7 +705,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const } rusb->PIPECFG = cfg; - rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num); + rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num); rusb->NRDYENB |= TU_BIT(num); rusb->BRDYENB |= TU_BIT(num); @@ -771,6 +758,17 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { //--------------------------------------------------------------------+ // ISR //--------------------------------------------------------------------+ +#if defined(__CCRX__) +TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) { + unsigned int count = 0; + while ((value & 1) == 0) { + value >>= 1; + count++; + } + return count; +} +#endif + void hcd_int_handler(uint8_t rhport, bool in_isr) { (void) in_isr; @@ -820,23 +818,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } } -#if defined(__CCRX__) - static const int Mod37BitPosition[] = { - -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, - 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, - 20, 8, 19, 18}; -#endif - if (is0 & RUSB2_INTSTS0_NRDY_Msk) { const unsigned m = rusb->NRDYENB; unsigned s = rusb->NRDYSTS & m; rusb->NRDYSTS = ~s; while (s) { -#if defined(__CCRX__) - const unsigned num = Mod37BitPosition[(-s & s) % 37]; -#else const unsigned num = __builtin_ctz(s); -#endif process_pipe_nrdy(rhport, num); s &= ~TU_BIT(num); } @@ -847,11 +834,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */ rusb->BRDYSTS = ~s; while (s) { -#if defined(__CCRX__) - const unsigned num = Mod37BitPosition[(-s & s) % 37]; -#else const unsigned num = __builtin_ctz(s); -#endif process_pipe_brdy(rhport, num); s &= ~TU_BIT(num); } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 7bf726f3f..cff328418 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -37,14 +37,20 @@ * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This * covers: * - * F04x, F072, F078, 070x6/B 1024 byte buffer + * F04x, F072, F078, F070x6/B 1024 byte buffer * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?) * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up + * G0 2048 byte buffer; 32-bit bus; host mode + * G4 1024 byte buffer + * H5 2048 byte buffer; 32-bit bus; host mode * L0x2, L0x3 1024 byte buffer * L1 512 byte buffer * L4x2, L4x3 1024 byte buffer - * G0 2048 byte buffer + * L5 1024 byte buffer + * U0 1024 byte buffer; 32-bit bus + * U535, U545 2048 byte buffer; 32-bit bus; host mode + * WB35, WB55 1024 byte buffer * * To use this driver, you must: * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) @@ -102,74 +108,53 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) +#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \ + !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0) #include "device/dcd.h" -#ifdef TUP_USBIP_FSDEV_STM32 +#if defined(TUP_USBIP_FSDEV_STM32) // Undefine to reduce the dependence on HAL #undef USE_HAL_DRIVER - #include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h" -#endif - -/***************************************************** - * Configuration - *****************************************************/ - -// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM -// (8u here would mean 8 IN and 8 OUT) -#ifndef MAX_EP_COUNT -# define MAX_EP_COUNT 8U + #include "fsdev_stm32.h" +#elif defined(TUP_USBIP_FSDEV_CH32) + #include "fsdev_ch32.h" +#else + #error "Unknown USB IP" #endif -// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it -// Both of these MUST be a multiple of 2, and are in byte units. -#ifndef DCD_STM32_BTABLE_BASE -# define DCD_STM32_BTABLE_BASE 0U -#endif +#include "fsdev_type.h" -#ifndef DCD_STM32_BTABLE_SIZE -# define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) -#endif +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ -/*************************************************** - * Checks, structs, defines, function definitions, etc. - */ -TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware"); -TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM"); -TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes"); //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ // One of these for every EP IN & OUT, uses a bit of RAM.... -typedef struct -{ - uint8_t * buffer; - tu_fifo_t * ff; +typedef struct { + uint8_t *buffer; + tu_fifo_t *ff; uint16_t total_len; uint16_t queued_len; - uint16_t pma_ptr; uint16_t max_packet_size; - uint16_t pma_alloc_size; - uint8_t ep_idx; // index for USB_EPnR register + uint8_t ep_idx; // index for USB_EPnR register + bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask } xfer_ctl_t; // EP allocator -typedef struct -{ +typedef struct { uint8_t ep_num; uint8_t ep_type; bool allocated[2]; } ep_alloc_t; -static xfer_ctl_t xfer_status[MAX_EP_COUNT][2]; - -static ep_alloc_t ep_alloc_status[STFSDEV_EP_COUNT]; - -static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; +static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2]; +static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT]; static uint8_t remoteWakeCountdown; // When wake is requested @@ -178,267 +163,86 @@ static uint8_t remoteWakeCountdown; // When wake is requested //--------------------------------------------------------------------+ // into the stack. -static void dcd_handle_bus_reset(void); -static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix); -static void dcd_ep_ctr_handler(void); +static void handle_bus_reset(uint8_t rhport); +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir); // PMA allocation/access -static uint8_t open_ep_count; static uint16_t ep_buf_ptr; ///< Points to first free memory location -static void dcd_pma_alloc_reset(void); -static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length); -static void dcd_pma_free(uint8_t ep_addr); -static void dcd_ep_free(uint8_t ep_addr); +static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf); static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes); -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes); +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes); -static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes); -static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes); +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); + +static void edpt0_open(uint8_t rhport); //--------------------------------------------------------------------+ // Inline helper //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr) { - uint8_t epnum = tu_edpt_number(ep_addr); - uint8_t dir = tu_edpt_dir(ep_addr); - // Fix -Werror=null-dereference - TU_ASSERT(epnum < MAX_EP_COUNT, &xfer_status[0][0]); - +TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint8_t dir) { return &xfer_status[epnum][dir]; } //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ - -void dcd_init (uint8_t rhport) -{ - /* Clocks should already be enabled */ - /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */ - - /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL. - * Here, the RM is followed. */ - - for(uint32_t i = 0; i<200; i++) // should be a few us - { +void dcd_init(uint8_t rhport) { + // Follow the RM mentions to use a special ordering of PDWN and FRES + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } + // Perform USB peripheral reset - USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; - for(uint32_t i = 0; i<200; i++) // should be a few us - { + FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } - USB->CNTR &= ~USB_CNTR_PDWN; + FSDEV_REG->CNTR &= ~USB_CNTR_PDWN; // Wait startup time, for F042 and F070, this is <= 1 us. - for(uint32_t i = 0; i<200; i++) // should be a few us - { + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } - USB->CNTR = 0; // Enable USB + FSDEV_REG->CNTR = 0; // Enable USB -#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address - USB->BTABLE = DCD_STM32_BTABLE_BASE; +#if !defined(FSDEV_BUS_32BIT) + // BTABLE register does not exist any more on 32-bit bus devices + FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE; #endif - USB->ISTR = 0; // Clear pending interrupts + + FSDEV_REG->ISTR = 0; // Clear pending interrupts // Reset endpoints to disabled - for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++) - { + for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. - pcd_set_endpoint(USB,i,0u); + ep_write(i, 0u); } - USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; - dcd_handle_bus_reset(); + FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; + handle_bus_reset(rhport); // Enable pull-up if supported - if ( dcd_connect ) dcd_connect(rhport); -} - -// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. -#if defined(USB_BCDR_DPPU) - -// Disable internal D+ PU -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - USB->BCDR &= ~(USB_BCDR_DPPU); -} - -// Enable internal D+ PU -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - USB->BCDR |= USB_BCDR_DPPU; -} - -#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 -// Disable internal D+ PU -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); -} - -// Enable internal D+ PU -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - SYSCFG->PMC |= SYSCFG_PMC_USB_PU; -} -#endif - -void dcd_sof_enable(uint8_t rhport, bool en) -{ - (void) rhport; - (void) en; - - if (en) - { - USB->CNTR |= USB_CNTR_SOFM; - } - else - { - USB->CNTR &= ~USB_CNTR_SOFM; - } -} - -// Enable device interrupt -void dcd_int_enable (uint8_t rhport) -{ - (void)rhport; - // Member here forces write to RAM before allowing ISR to execute - __DSB(); - __ISB(); -#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \ - CFG_TUSB_MCU == OPT_MCU_STM32L4 - NVIC_EnableIRQ(USB_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 - NVIC_EnableIRQ(USB_LP_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 - // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from - // shared USB/CAN IRQs to separate CAN and USB IRQs. - // This dynamically checks if this remap is active to enable the right IRQs. - #ifdef SYSCFG_CFGR1_USB_IT_RMP - if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) - { - NVIC_EnableIRQ(USB_HP_IRQn); - NVIC_EnableIRQ(USB_LP_IRQn); - NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); - } - else - #endif - { - NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn); - NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); - NVIC_EnableIRQ(USBWakeUp_IRQn); - } -#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 - NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn); - NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); - NVIC_EnableIRQ(USBWakeUp_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 - NVIC_EnableIRQ(USB_HP_IRQn); - NVIC_EnableIRQ(USB_LP_IRQn); - NVIC_EnableIRQ(USBWakeUp_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 - #ifdef STM32G0B0xx - NVIC_EnableIRQ(USB_IRQn); - #else - NVIC_EnableIRQ(USB_UCPD1_2_IRQn); - #endif - -#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 - NVIC_EnableIRQ(USB_DRD_FS_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32WB - NVIC_EnableIRQ(USB_HP_IRQn); - NVIC_EnableIRQ(USB_LP_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 - NVIC_EnableIRQ(USB_FS_IRQn); - -#else - #error Unknown arch in USB driver -#endif + dcd_connect(rhport); } -// Disable device interrupt -void dcd_int_disable(uint8_t rhport) -{ +void dcd_sof_enable(uint8_t rhport, bool en) { (void)rhport; -#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \ - CFG_TUSB_MCU == OPT_MCU_STM32L4 - NVIC_DisableIRQ(USB_IRQn); -#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 - NVIC_DisableIRQ(USB_LP_IRQn); -#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 - // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from - // shared USB/CAN IRQs to separate CAN and USB IRQs. - // This dynamically checks if this remap is active to disable the right IRQs. - #ifdef SYSCFG_CFGR1_USB_IT_RMP - if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) - { - NVIC_DisableIRQ(USB_HP_IRQn); - NVIC_DisableIRQ(USB_LP_IRQn); - NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); - } - else - #endif - { - NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn); - NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn); - NVIC_DisableIRQ(USBWakeUp_IRQn); + if (en) { + FSDEV_REG->CNTR |= USB_CNTR_SOFM; + } else { + FSDEV_REG->CNTR &= ~USB_CNTR_SOFM; } -#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 - NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn); - NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); - NVIC_DisableIRQ(USBWakeUp_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 - NVIC_DisableIRQ(USB_HP_IRQn); - NVIC_DisableIRQ(USB_LP_IRQn); - NVIC_DisableIRQ(USBWakeUp_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 - #ifdef STM32G0B0xx - NVIC_DisableIRQ(USB_IRQn); - #else - NVIC_DisableIRQ(USB_UCPD1_2_IRQn); - #endif - -#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 - NVIC_DisableIRQ(USB_DRD_FS_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32WB - NVIC_DisableIRQ(USB_HP_IRQn); - NVIC_DisableIRQ(USB_LP_IRQn); - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 - NVIC_DisableIRQ(USB_FS_IRQn); - -#else - #error Unknown arch in USB driver -#endif - - // CMSIS has a membar after disabling interrupts } // Receive Set Address request, mcu port must also include status IN response -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void) rhport; - (void) dev_addr; +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void)dev_addr; // Respond with status dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); @@ -447,46 +251,17 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) // do it at dcd_edpt0_status_complete() } -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; +void dcd_remote_wakeup(uint8_t rhport) { + (void)rhport; - USB->CNTR |= USB_CNTR_RESUME; + FSDEV_REG->CNTR |= USB_CNTR_RESUME; remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. } -static const tusb_desc_endpoint_t ep0OUT_desc = -{ - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - - .bEndpointAddress = 0x00, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bInterval = 0 -}; - -static const tusb_desc_endpoint_t ep0IN_desc = -{ - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - - .bEndpointAddress = 0x80, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, - .bInterval = 0 -}; - -static void dcd_handle_bus_reset(void) -{ - //__IO uint16_t * const epreg = &(EPREG(0)); - USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag - - for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++) - { - // Clear all EPREG (or maybe this is automatic? I'm not sure) - pcd_set_endpoint(USB,i,0u); +static void handle_bus_reset(uint8_t rhport) { + FSDEV_REG->DADDR = 0u; // disable USB Function + for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { // Clear EP allocation status ep_alloc_status[i].ep_num = 0xFF; ep_alloc_status[i].ep_type = 0xFF; @@ -494,47 +269,52 @@ static void dcd_handle_bus_reset(void) ep_alloc_status[i].allocated[1] = false; } - dcd_pma_alloc_reset(); - dcd_edpt_open (0, &ep0OUT_desc); - dcd_edpt_open (0, &ep0IN_desc); + // Reset PMA allocation + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT; + + edpt0_open(rhport); // open control endpoint (both IN & OUT) - USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. + FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function } // Handle CTR interrupt for the TX/IN direction -// -// Upon call, (wIstr & USB_ISTR_DIR) == 0U -static void dcd_ep_ctr_tx_handler(uint32_t wIstr) -{ - uint32_t EPindex = wIstr & USB_ISTR_EP_ID; - uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); - uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; +static void handle_ctr_tx(uint32_t ep_id) { + uint32_t ep_reg = ep_read(ep_id) & USB_EPREG_MASK; - // Verify the CTR_TX bit is set. This was in the ST Micro code, - // but I'm not sure it's actually necessary? - if((wEPRegVal & USB_EP_CTR_TX) == 0U) - { - return; - } + // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary? + TU_VERIFY(ep_reg & USB_EP_CTR_TX, ); - /* clear int flag */ - pcd_clear_tx_ep_ctr(USB, EPindex); + uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; + uint8_t ep_addr = (ep_reg & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN); - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); - if((xfer->total_len != xfer->queued_len)) /* TX not complete */ - { - dcd_transmit_packet(xfer, EPindex); + if (ep_is_iso(ep_reg)) { + // Ignore spurious interrupts that we don't schedule + // host can send IN token while there is no data to send, since ISO does not have NAK + // this will result to zero length packet --> trigger interrupt (which cannot be masked) + if (!xfer->iso_in_sending) { + return; + } + xfer->iso_in_sending = false; + uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1; + btable_set_count(ep_id, buf_id, 0); } - else /* TX Complete */ - { + + if (xfer->total_len != xfer->queued_len) { + dcd_transmit_packet(xfer, ep_id); // also clear CTR bit + } else { dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); + + // Clear CTR TX and reserved CTR RX + ep_reg = (ep_reg & ~USB_EP_CTR_TX) | USB_EP_CTR_RX; + + ep_write(ep_id, ep_reg); } } // Handle CTR interrupt for the RX/OUT direction -// Upon call, (wIstr & USB_ISTR_DIR) == 0U -static void dcd_ep_ctr_rx_handler(uint32_t wIstr) { - #ifdef FSDEV_BUS_32BIT +static void handle_ctr_rx(uint32_t ep_id) { +#ifdef FSDEV_BUS_32BIT /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers * Description: @@ -544,200 +324,157 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr) { * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code - * also takes time, so we'll wait 40 cycles (count = 20). + * also takes time, so we'll wait 60 cycles (count = 20). * - Since Low Speed mode is not supported/popular, we will ignore it for now. * * Note: this errata also seems to apply to G0, U5, H5 etc. */ volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver while (cycle_count > 0U) { - cycle_count--; // each count take 2 cycle (1 cycle for sub, 1 cycle for compare/jump) + cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) } - #endif +#endif - uint32_t EPindex = wIstr & USB_ISTR_EP_ID; - uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); - uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD; + uint32_t ep_reg = ep_read(ep_id); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary? + TU_VERIFY(ep_reg & USB_EP_CTR_RX, ); + ep_reg = (ep_reg & ~USB_EP_CTR_RX) | USB_EP_CTR_TX; // Clear CTR RX and reserved CTR TX - // Verify the CTR_RX bit is set. This was in the ST Micro code, - // but I'm not sure it's actually necessary? - if((wEPRegVal & USB_EP_CTR_RX) == 0U) { - return; - } + uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; - if((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */ - { - uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex); - /* Get SETUP Packet*/ - if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again. - { - // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here) - pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK); - pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK); -#ifdef FSDEV_BUS_32BIT - dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true); -#else - // The setup_received function uses memcpy, so this must first copy the setup data into - // user memory, to allow for the 32-bit access that memcpy performs. - uint8_t userMemBuf[8]; - dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB,EPindex), 8); - dcd_event_setup_received(0, (uint8_t*)userMemBuf, true); -#endif - } - } - else - { - uint32_t count; - /* Read from correct register when ISOCHRONOUS (double buffered) */ - if ( (wEPRegVal & USB_EP_DTOG_RX) && ( (wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) { - count = pcd_get_ep_tx_cnt(USB, EPindex); - } else { - count = pcd_get_ep_rx_cnt(USB, EPindex); - } + if (ep_reg & USB_EP_SETUP) { + uint32_t count = btable_get_count(ep_id, BTABLE_BUF_RX); + // Setup packet should always be 8 bytes. If not, ignore it, and try again. + if (count == 8) { + uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX); + uint32_t setup_packet[2]; + dcd_read_packet_memory(setup_packet, rx_addr, 8); + dcd_event_setup_received(0, (uint8_t*) setup_packet, true); - TU_ASSERT(count <= xfer->max_packet_size, /**/); + // Reset EP to NAK (in case it had been stalling) + ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK); + ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK); - // Clear RX CTR interrupt flag - if(ep_addr != 0u) - { - pcd_clear_rx_ep_ctr(USB, EPindex); + ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_IN, 1); + ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_OUT, 1); + } else { + ep_reg &= USB_EPREG_MASK; // reversed all toggle } + } else { + ep_reg &= USB_EPRX_STAT | USB_EPREG_MASK; // reversed all toggle except RX Status - if (count != 0U) - { - uint16_t addr = pcd_get_ep_rx_address(USB, EPindex); - - if (xfer->ff) - { - dcd_read_packet_memory_ff(xfer->ff, addr, count); - } - else - { - dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); - } + bool const is_iso = ep_is_iso(ep_reg); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT); - xfer->queued_len = (uint16_t)(xfer->queued_len + count); + uint8_t buf_id; + if (is_iso) { + buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered + } else { + buf_id = BTABLE_BUF_RX; } + uint32_t rx_count = btable_get_count(ep_id, buf_id); + uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id); - if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) - { - /* RX COMPLETE */ - dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); - // Though the host could still send, we don't know. - // Does the bulk pipe need to be reset to valid to allow for a ZLP? - } - else - { - uint32_t remaining = (uint32_t)xfer->total_len - (uint32_t)xfer->queued_len; - if(remaining >= xfer->max_packet_size) { - pcd_set_ep_rx_bufsize(USB, EPindex,xfer->max_packet_size); + if (rx_count != 0) { + if (xfer->ff) { + dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count); } else { - pcd_set_ep_rx_bufsize(USB, EPindex,remaining); + dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count); } - if (!((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS)) { - /* Set endpoint active again for receiving more data. - * Note that isochronous endpoints stay active always */ - pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); - } + xfer->queued_len = (uint16_t)(xfer->queued_len + rx_count); } - } - // For EP0, prepare to receive another SETUP packet. - // Clear CTR last so that a new packet does not overwrite the packing being read. - // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared) - if(ep_addr == 0u) - { - // Always be prepared for a status packet... - pcd_set_ep_rx_bufsize(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); - pcd_clear_rx_ep_ctr(USB, EPindex); - } -} - -static void dcd_ep_ctr_handler(void) { - uint32_t wIstr; + if ((rx_count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { + uint8_t const ep_addr = ep_num; + // all bytes received or short packet + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); - /* stay in loop while pending interrupts */ - while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { - if ((wIstr & USB_ISTR_DIR) == 0U) { - /* TX/IN */ - dcd_ep_ctr_tx_handler(wIstr); + if (ep_num == 0) { + // prepared for status packet + btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE); + } + ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK); } else { - /* RX/OUT*/ - dcd_ep_ctr_rx_handler(wIstr); + // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always + if (!is_iso) { + uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt); + } + ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_VALID); } } + + ep_write(ep_id, ep_reg); } void dcd_int_handler(uint8_t rhport) { - - (void) rhport; - - uint32_t int_status = USB->ISTR; - //const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP - // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; - // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) + uint32_t int_status = FSDEV_REG->ISTR; + // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP + // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; + // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) // The ST driver loops here on the CTR bit, but that loop has been moved into the // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't // be triggered repeatedly. /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ - if(int_status & USB_ISTR_SOF) { - USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; - dcd_event_sof(0, USB->FNR & USB_FNR_FN, true); + if (int_status & USB_ISTR_SOF) { + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; + dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true); } - if(int_status & USB_ISTR_RESET) { + if (int_status & USB_ISTR_RESET) { // USBRST is start of reset. - USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; - dcd_handle_bus_reset(); + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + handle_bus_reset(rhport); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); return; // Don't do the rest of the things here; perhaps they've been cleared? } - if (int_status & USB_ISTR_CTR) - { - /* servicing of the endpoint correct transfer interrupt */ - /* clear of the CTR flag into the sub */ - dcd_ep_ctr_handler(); - } - - if (int_status & USB_ISTR_WKUP) - { - USB->CNTR &= ~USB_CNTR_LPMODE; - USB->CNTR &= ~USB_CNTR_FSUSP; + if (int_status & USB_ISTR_WKUP) { + FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE; + FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP; - USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); } - if (int_status & USB_ISTR_SUSP) - { + if (int_status & USB_ISTR_SUSP) { /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring, * these events cannot be differentiated, so we only trigger suspend. */ /* Force low-power mode in the macrocell */ - USB->CNTR |= USB_CNTR_FSUSP; - USB->CNTR |= USB_CNTR_LPMODE; + FSDEV_REG->CNTR |= USB_CNTR_FSUSP; + FSDEV_REG->CNTR |= USB_CNTR_LPMODE; /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ - USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } - if(int_status & USB_ISTR_ESOF) { - if(remoteWakeCountdown == 1u) - { - USB->CNTR &= ~USB_CNTR_RESUME; + if (int_status & USB_ISTR_ESOF) { + if (remoteWakeCountdown == 1u) { + FSDEV_REG->CNTR &= ~USB_CNTR_RESUME; } - if(remoteWakeCountdown > 0u) - { + if (remoteWakeCountdown > 0u) { remoteWakeCountdown--; } - USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + } + + // loop to handle all pending CTR interrupts + while (int_status & USB_ISTR_CTR) { + uint32_t const ep_id = int_status & USB_ISTR_EP_ID; + + if ((int_status & USB_ISTR_DIR) == 0U) { + handle_ctr_tx(ep_id); // TX/IN + } else { + handle_ctr_rx(ep_id); // RX/OUT or both (RX/TX !!) + } + + int_status = FSDEV_REG->ISTR; } } @@ -747,137 +484,67 @@ void dcd_int_handler(uint8_t rhport) { // Invoked when a control transfer's status stage is complete. // May help DCD to prepare for next control transfer, this API is optional. -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) -{ - (void) rhport; +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { + (void)rhport; if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS ) - { - uint8_t const dev_addr = (uint8_t) request->wValue; - - // Setting new address after the whole request is complete - USB->DADDR &= ~USB_DADDR_ADD; - USB->DADDR |= dev_addr; // leave the enable bit set - } -} - -static void dcd_pma_alloc_reset(void) -{ - open_ep_count = 0; - ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each) - //TU_LOG2("dcd_pma_alloc_reset()\r\n"); - for(uint32_t i=0; i<MAX_EP_COUNT; i++) - { - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_alloc_size = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U; + request->bRequest == TUSB_REQ_SET_ADDRESS) { + uint8_t const dev_addr = (uint8_t)request->wValue; + FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr); } } /*** * Allocate a section of PMA - * - * If the EP number has already been allocated, and the new allocation - * is larger than the old allocation, then this will fail with a TU_ASSERT. - * (This is done to simplify the code. More complicated algorithms could be used) - * + * In case of double buffering, high 16bit is the address of 2nd buffer * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. */ -static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length) +static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) { - xfer_ctl_t* epXferCtl = xfer_ctl_ptr(ep_addr); - - if(epXferCtl->pma_alloc_size != 0U) - { - //TU_LOG2("dcd_pma_alloc(%x,%x)=%x (cached)\r\n",ep_addr,length,epXferCtl->pma_ptr); - // Previously allocated - TU_ASSERT(length <= epXferCtl->pma_alloc_size, 0xFFFF); // Verify no larger than previous alloc - return epXferCtl->pma_ptr; - } + uint8_t blsize, num_block; + uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block); + (void) blsize; + (void) num_block; - // Ensure allocated buffer is aligned -#ifdef FSDEV_BUS_32BIT - length = (length + 3) & ~0x03; -#else - length = (length + 1) & ~0x01; -#endif + uint32_t addr = ep_buf_ptr; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer - open_ep_count++; - - uint16_t addr = ep_buf_ptr; - ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + if (dbuf) { + addr |= ((uint32_t)ep_buf_ptr) << 16; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer + } - // Verify no overflow + // Verify packet buffer is not overflowed TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); - epXferCtl->pma_ptr = addr; - epXferCtl->pma_alloc_size = length; - //TU_LOG1("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr); - return addr; } /*** - * Free a block of PMA space - */ -static void dcd_pma_free(uint8_t ep_addr) -{ - // Presently, this should never be called for EP0 IN/OUT - TU_ASSERT(open_ep_count > 2, /**/); - TU_ASSERT(xfer_ctl_ptr(ep_addr)->max_packet_size != 0, /**/); - open_ep_count--; - - // If count is 2, only EP0 should be open, so allocations can be mostly reset. - - if(open_ep_count == 2) - { - ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT + 2*CFG_TUD_ENDPOINT0_SIZE; // 8 bytes per endpoint (two TX and two RX words, each), and EP0 - - // Skip EP0 - for(uint32_t i=1; i<MAX_EP_COUNT; i++) - { - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_alloc_size = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U; - xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U; - } - } -} - -/*** * Allocate hardware endpoint */ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) { uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++) - { + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { // Check if already allocated - if(ep_alloc_status[i].allocated[dir] && - ep_alloc_status[i].ep_type == ep_type && - ep_alloc_status[i].ep_num == epnum) - { + if (ep_alloc_status[i].allocated[dir] && + ep_alloc_status[i].ep_type == ep_type && + ep_alloc_status[i].ep_num == epnum) { return i; } // If EP of current direction is not allocated // Except for ISO endpoint, both direction should be free - if(!ep_alloc_status[i].allocated[dir] && - (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) - { + if (!ep_alloc_status[i].allocated[dir] && + (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) { // Check if EP number is the same - if(ep_alloc_status[i].ep_num == 0xFF || - ep_alloc_status[i].ep_num == epnum) - { + if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { // One EP pair has to be the same type - if(ep_alloc_status[i].ep_type == 0xFF || - ep_alloc_status[i].ep_type == ep_type) - { + if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) { ep_alloc_status[i].ep_num = epnum; ep_alloc_status[i].ep_type = ep_type; ep_alloc_status[i].allocated[dir] = true; @@ -892,469 +559,420 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) TU_ASSERT(0); } -/*** - * Free hardware endpoint - */ -static void dcd_ep_free(uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); +void edpt0_open(uint8_t rhport) { + (void) rhport; - for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++) - { - // Check if EP number & dir are the same - if(ep_alloc_status[i].ep_num == epnum && - ep_alloc_status[i].allocated[dir] == dir) - { - ep_alloc_status[i].allocated[dir] = false; - // Reset entry if ISO endpoint or both direction are free - if(ep_alloc_status[i].ep_type == TUSB_XFER_ISOCHRONOUS || - !ep_alloc_status[i].allocated[dir ^ 1]) - { - ep_alloc_status[i].ep_num = 0xFF; - ep_alloc_status[i].ep_type = 0xFF; + dcd_ep_alloc(0x0, TUSB_XFER_CONTROL); + dcd_ep_alloc(0x80, TUSB_XFER_CONTROL); - return; - } - } - } -} + xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][0].ep_idx = 0; -// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, -// so I'm using the #define from HAL here, instead. + xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][1].ep_idx = 0; -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ - (void)rhport; - uint8_t const ep_idx = dcd_ep_alloc(p_endpoint_desc->bEndpointAddress, p_endpoint_desc->bmAttributes.xfer); - uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); - const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); - uint16_t pma_addr; - uint32_t wType; + uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); + uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); - TU_ASSERT(ep_idx < STFSDEV_EP_COUNT); - TU_ASSERT(buffer_size <= 1024); + btable_set_addr(0, BTABLE_BUF_RX, pma_addr0); + btable_set_addr(0, BTABLE_BUF_TX, pma_addr1); - // Set type - switch(p_endpoint_desc->bmAttributes.xfer) { - case TUSB_XFER_CONTROL: - wType = USB_EP_CONTROL; - break; - case TUSB_XFER_ISOCHRONOUS: - wType = USB_EP_ISOCHRONOUS; - break; - case TUSB_XFER_BULK: - wType = USB_EP_CONTROL; - break; + uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK; + ep_reg |= USB_EP_CONTROL; + ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK); + ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK); + // no need to explicitly set DTOG bits since we aren't masked DTOG bit - case TUSB_XFER_INTERRUPT: - wType = USB_EP_INTERRUPT; - break; + // prepare for setup packet + btable_set_rx_bufsize(0, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE); - default: - TU_ASSERT(false); - } + ep_write(0, ep_reg); +} - pcd_set_eptype(USB, ep_idx, wType); - pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress)); - // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint) - // or being double-buffered (bulk endpoints) - pcd_clear_ep_kind(USB,0); +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { + (void)rhport; + uint8_t const ep_addr = desc_ep->bEndpointAddress; + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(desc_ep); + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer); + TU_ASSERT(ep_idx < FSDEV_EP_COUNT); - /* Create a packet memory buffer area. For isochronous endpoints, - * use the same buffer as the double buffer, essentially disabling double buffering */ - pma_addr = dcd_pma_alloc(p_endpoint_desc->bEndpointAddress, buffer_size); + uint32_t ep_reg = FSDEV_REG->ep[ep_idx].reg & ~USB_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_RX | USB_EP_CTR_TX; - if( (dir == TUSB_DIR_IN) || (wType == USB_EP_ISOCHRONOUS) ) - { - pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size); - pcd_clear_tx_dtog(USB, ep_idx); - } + // Set type + switch (desc_ep->bmAttributes.xfer) { + case TUSB_XFER_BULK: + ep_reg |= USB_EP_BULK; + break; + case TUSB_XFER_INTERRUPT: + ep_reg |= USB_EP_INTERRUPT; + break; - if( (dir == TUSB_DIR_OUT) || (wType == USB_EP_ISOCHRONOUS) ) - { - pcd_set_ep_rx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size); - pcd_clear_rx_dtog(USB, ep_idx); + default: + // Note: ISO endpoint should use alloc / active functions + TU_ASSERT(false); } - /* Enable endpoint */ - if (dir == TUSB_DIR_IN) - { - if(wType == USB_EP_ISOCHRONOUS) { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - } else { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); - } - } else - { - if(wType == USB_EP_ISOCHRONOUS) { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); - } else { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); - } + /* Create a packet memory buffer area. */ + uint16_t pma_addr = dcd_pma_alloc(packet_size, false); + btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + xfer->max_packet_size = packet_size; + xfer->ep_idx = ep_idx; + + ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK); + ep_reg = ep_add_dtog(ep_reg, dir, 0); + + // reserve other direction toggle bits + if (dir == TUSB_DIR_IN) { + ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX); + } else { + ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX); } - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size; - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx = ep_idx; + ep_write(ep_idx, ep_reg); return true; } -void dcd_edpt_close_all (uint8_t rhport) -{ - (void) rhport; - // TODO implement dcd_edpt_close_all() -} - -/** - * Close an endpoint. - * - * This function may be called with interrupts enabled or disabled. - * - * This also clears transfers in progress, should there be any. - */ -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) +void dcd_edpt_close_all(uint8_t rhport) { (void)rhport; - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); - uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - - if(dir == TUSB_DIR_IN) - { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - } - else - { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) { + // Reset endpoint + ep_write(i, 0); + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; } - dcd_ep_free(ep_addr); - - dcd_pma_free(ep_addr); + // Reset PMA allocation + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; } -bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) -{ +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void)rhport; - TU_ASSERT(largest_packet_size <= 1024); - + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); - const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size); - - /* Create a packet memory buffer area. For isochronous endpoints, - * use the same buffer as the double buffer, essentially disabling double buffering */ - uint16_t pma_addr = dcd_pma_alloc(ep_addr, buffer_size); - xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, + for smaller devices double buffering occupy too much space. */ + uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true); +#if FSDEV_PMA_SIZE > 1024u + uint16_t pma_addr2 = pma_addr >> 16; +#else + uint16_t pma_addr2 = pma_addr; +#endif - pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); + btable_set_addr(ep_idx, 0, pma_addr); + btable_set_addr(ep_idx, 1, pma_addr2); - pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_address(USB, ep_idx, pma_addr); + xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); + xfer->ep_idx = ep_idx; return true; } -bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) -{ +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { (void)rhport; - uint8_t const ep_idx = xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx; - uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); - const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); + uint8_t const ep_addr = desc_ep->bEndpointAddress; + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); - /* Disable endpoint */ - if(dir == TUSB_DIR_IN) - { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - } - else - { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); - } + uint8_t const ep_idx = xfer->ep_idx; - pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress)); - // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint) - // or being double-buffered (bulk endpoints) - pcd_clear_ep_kind(USB,0); + xfer->max_packet_size = tu_edpt_packet_size(desc_ep); - pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size); - pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size); - pcd_clear_tx_dtog(USB, ep_idx); - pcd_clear_rx_dtog(USB, ep_idx); + uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_RX | USB_EP_CTR_TX; + ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED); + ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED); + ep_reg = ep_add_dtog(ep_reg, dir, 0); + ep_reg = ep_add_dtog(ep_reg, 1-dir, 1); - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size; + ep_write(ep_idx, ep_reg); return true; } // Currently, single-buffered, and only 64 bytes at a time (max) +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) { + uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + uint16_t ep_reg = ep_read(ep_ix) | EP_CTR_TXRX; + bool const is_iso = ep_is_iso(ep_reg); -static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix) -{ - uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len); - - if(len > xfer->max_packet_size) // max packet size for FS transfer - { - len = xfer->max_packet_size; + uint8_t buf_id; + if (is_iso) { + buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0; + } else { + buf_id = BTABLE_BUF_TX; } + uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id); + btable_set_count(ep_ix, buf_id, len); - uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); - uint16_t addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); - - if (xfer->ff) - { + if (xfer->ff) { dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); - } - else - { + } else { dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); } xfer->queued_len = (uint16_t)(xfer->queued_len + len); - /* Write into correct register when ISOCHRONOUS (double buffered) */ - if ( (ep_reg & USB_EP_DTOG_TX) && ( (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) { - pcd_set_ep_rx_cnt(USB, ep_ix, len); - } else { - pcd_set_ep_tx_cnt(USB, ep_ix, len); - } + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN); + ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_VALID); + ep_reg = ep_clear_ctr(ep_reg, TUSB_DIR_IN); - pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); + dcd_int_disable(0); + ep_write(ep_ix, ep_reg); + if (is_iso) { + xfer->iso_in_sending = true; + } + dcd_int_enable(0); } -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ +static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir) { (void) rhport; - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer->buffer = buffer; - xfer->ff = NULL; - xfer->total_len = total_bytes; - xfer->queued_len = 0; - if ( dir == TUSB_DIR_OUT ) - { - // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid - // buffer for the control endpoint. - if (ep_idx == 0 && buffer == NULL) - { - xfer->buffer = (uint8_t*)_setup_packet; - } + if (dir == TUSB_DIR_IN) { + dcd_transmit_packet(xfer, ep_idx); + } else { + uint32_t cnt = (uint32_t) tu_min16(xfer->total_len, xfer->max_packet_size); + uint16_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX; + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); // keep CTR TX, clear CTR RX + ep_reg = ep_add_status(ep_reg, dir, EP_STAT_VALID); - if(total_bytes > xfer->max_packet_size) - { - pcd_set_ep_rx_bufsize(USB,ep_idx,xfer->max_packet_size); + if (ep_is_iso(ep_reg)) { + btable_set_rx_bufsize(ep_idx, 0, cnt); + btable_set_rx_bufsize(ep_idx, 1, cnt); } else { - pcd_set_ep_rx_bufsize(USB,ep_idx,total_bytes); + btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt); } - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); - } - else // IN - { - dcd_transmit_packet(xfer,ep_idx); + + ep_write(ep_idx, ep_reg); } + return true; } -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) -{ - (void) rhport; +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_num, dir); +} - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); - uint8_t const epnum = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); xfer->buffer = NULL; - xfer->ff = ff; + xfer->ff = ff; xfer->total_len = total_bytes; xfer->queued_len = 0; - if ( dir == TUSB_DIR_OUT ) - { - if(total_bytes > xfer->max_packet_size) - { - pcd_set_ep_rx_bufsize(USB,epnum,xfer->max_packet_size); - } else { - pcd_set_ep_rx_bufsize(USB,epnum,total_bytes); - } - pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID); - } - else // IN - { - dcd_transmit_packet(xfer,epnum); - } - return true; + return edpt_xfer(rhport, ep_num, dir); } -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - if (dir == TUSB_DIR_IN) - { // IN - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); - } - else - { // OUT - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); - } + uint32_t ep_reg = ep_read(ep_idx); + ep_reg |= USB_EP_CTR_RX | USB_EP_CTR_TX; // reserve CTR bits + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); + ep_reg = ep_add_status(ep_reg, dir, EP_STAT_STALL); + + ep_write(ep_idx, ep_reg); } -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_num = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); uint8_t const ep_idx = xfer->ep_idx; - uint8_t const dir = tu_edpt_dir(ep_addr); - if (dir == TUSB_DIR_IN) - { // IN - if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { - pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); - } + uint32_t ep_reg = ep_read(ep_idx) | EP_CTR_TXRX; + ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir); - /* Reset to DATA0 if clearing stall condition. */ - pcd_clear_tx_dtog(USB, ep_idx); - } - else - { // OUT - if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { - pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); - } - /* Reset to DATA0 if clearing stall condition. */ - pcd_clear_rx_dtog(USB, ep_idx); + if (!ep_is_iso(ep_reg)) { + ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK); } + ep_reg = ep_add_dtog(ep_reg, dir, 0); // Reset to DATA0 + + ep_write(ep_idx, ep_reg); } #ifdef FSDEV_BUS_32BIT -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) -{ - const uint8_t* srcVal = src; - volatile uint32_t* dst32 = (volatile uint32_t*)(USB_PMAADDR + dst); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) { + const uint8_t *src8 = src; + volatile uint32_t *pma32 = (volatile uint32_t *)(USB_PMAADDR + dst); for (uint32_t n = wNBytes / 4; n > 0; --n) { - *dst32++ = tu_unaligned_read32(srcVal); - srcVal += 4; + *pma32++ = tu_unaligned_read32(src8); + src8 += 4; } - wNBytes = wNBytes & 0x03; - if (wNBytes) - { - uint32_t wrVal = *srcVal; - wNBytes--; + uint16_t odd = wNBytes & 0x03; + if (odd) { + uint32_t wrVal = *src8; + odd--; - if (wNBytes) - { - wrVal |= *++srcVal << 8; - wNBytes--; + if (odd) { + wrVal |= *++src8 << 8; + odd--; - if (wNBytes) - { - wrVal |= *++srcVal << 16; + if (odd) { + wrVal |= *++src8 << 16; } } - *dst32 = wrVal; + *pma32 = wrVal; + } + + return true; +} + +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) { + uint8_t *dst8 = dst; + volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + tu_unaligned_write32(dst8, *src32++); + dst8 += 4; + } + + uint16_t odd = wNBytes & 0x03; + if (odd) { + uint32_t rdVal = *src32; + + *dst8 = tu_u32_byte0(rdVal); + odd--; + + if (odd) { + *++dst8 = tu_u32_byte1(rdVal); + odd--; + + if (odd) { + *++dst8 = tu_u32_byte2(rdVal); + } + } } return true; } + #else // Packet buffer access can only be 8- or 16-bit. /** - * @brief Copy a buffer from user memory area to packet memory area (PMA). - * This uses byte-access for user memory (so support non-aligned buffers) - * and 16-bit access for packet memory. - * @param dst, byte address in PMA; must be 16-bit aligned - * @param src pointer to user memory area. - * @param wPMABufAddr address into PMA. - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) -{ - uint32_t n = (uint32_t)wNBytes >> 1U; - uint16_t temp1, temp2; - const uint8_t * srcVal; + * @brief Copy a buffer from user memory area to packet memory area (PMA). + * This uses un-aligned for user memory and 16-bit access for packet memory. + * @param dst, byte address in PMA; must be 16-bit aligned + * @param src pointer to user memory area. + * @param wPMABufAddr address into PMA. + * @param nbytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) { + uint32_t n16 = (uint32_t)nbytes >> 1U; + const uint8_t *src8 = src; + fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * dst); - // The GCC optimizer will combine access to 32-bit sizes if we let it. Force - // it volatile so that it won't do that. - __IO uint16_t *pdwVal; + while (n16--) { + pma16->u16 = tu_unaligned_read16(src8); + src8 += 2; + pma16++; + } + + if (nbytes & 0x01) { + pma16->u16 = (uint16_t) *src8; + } - srcVal = src; - pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; + return true; +} - while (n--) - { - temp1 = (uint16_t)*srcVal; - srcVal++; - temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ; - *pdwVal = temp2; - pdwVal += FSDEV_PMA_STRIDE; - srcVal++; +/** + * @brief Copy a buffer from packet memory area (PMA) to user memory area. + * Uses unaligned for system memory and 16-bit access of packet memory + * @param nbytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) { + uint32_t n16 = (uint32_t)nbytes >> 1U; + fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * src); + uint8_t *dst8 = (uint8_t *)dst; + + while (n16--) { + uint16_t temp16 = pma16->u16; + tu_unaligned_write16(dst8, temp16); + dst8 += 2; + pma16++; } - if (wNBytes) - { - temp1 = *srcVal; - *pdwVal = temp1; + if (nbytes & 0x01) { + *dst8++ = tu_u16_low(pma16->u16); } return true; } + #endif /** - * @brief Copy from FIFO to packet memory area (PMA). - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes) -{ + * @brief Copy from FIFO to packet memory area (PMA). + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) { // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies tu_fifo_buffer_info_t info; tu_fifo_get_read_info(ff, &info); - uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, // last lin byte will be combined with wrapped part // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit) #ifdef FSDEV_BUS_32BIT - if((cnt_lin & 0x03) && cnt_wrap) - { + if ((cnt_lin & 0x03) && cnt_wrap) { // Copy first linear part - dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x03); - dst += cnt_lin &~0x03; + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03); + dst += cnt_lin & ~0x03; // Copy last linear bytes & first wrapped bytes to buffer uint32_t i; uint8_t tmp[4]; - for (i = 0; i < (cnt_lin & 0x03); i++) - { - tmp[i] = ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i]; + for (i = 0; i < (cnt_lin & 0x03); i++) { + tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i]; } uint32_t wCnt = cnt_wrap; - for (; i < 4 && wCnt > 0; i++, wCnt--) - { - tmp[i] = *(uint8_t*)info.ptr_wrap; - info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + tmp[i] = *(uint8_t *)info.ptr_wrap; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; } // Write unaligned buffer @@ -1362,33 +980,31 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN dst += 4; // Copy rest of wrapped byte - if (wCnt) - dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); + if (wCnt) { + dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); + } } #else - if((cnt_lin & 0x01) && cnt_wrap) - { + if ((cnt_lin & 0x01) && cnt_wrap) { // Copy first linear part - dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x01); - dst += cnt_lin &~0x01; + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01); + dst += cnt_lin & ~0x01; // Copy last linear byte & first wrapped byte - uint16_t tmp = ((uint8_t*)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t*)info.ptr_wrap)[0]) << 8U); + uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U); dcd_write_packet_memory(dst, &tmp, 2); dst += 2; // Copy rest of wrapped byte - dcd_write_packet_memory(dst, ((uint8_t*)info.ptr_wrap) + 1, cnt_wrap - 1); + dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1); } #endif - else - { + else { // Copy linear part dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin); dst += info.len_lin; - if(info.len_wrap) - { + if (info.len_wrap) { // Copy wrapped byte dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap); } @@ -1399,101 +1015,29 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN return true; } -#ifdef FSDEV_BUS_32BIT -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) -{ - uint8_t* dstVal = dst; - volatile uint32_t* src32 = (volatile uint32_t*)(USB_PMAADDR + src); - - for (uint32_t n = wNBytes / 4; n > 0; --n) { - tu_unaligned_write32(dstVal, *src32++); - dstVal += 4; - } - - wNBytes = wNBytes & 0x03; - if (wNBytes) - { - uint32_t rdVal = *src32; - - *dstVal = tu_u32_byte0(rdVal); - wNBytes--; - - if (wNBytes) - { - *++dstVal = tu_u32_byte1(rdVal); - wNBytes--; - - if (wNBytes) - { - *++dstVal = tu_u32_byte2(rdVal); - } - } - } - - return true; -} -#else /** - * @brief Copy a buffer from packet memory area (PMA) to user memory area. - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) -{ - uint32_t n = (uint32_t)wNBytes >> 1U; - // The GCC optimizer will combine access to 32-bit sizes if we let it. Force - // it volatile so that it won't do that. - __IO const uint16_t *pdwVal; - uint32_t temp; - - pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; - uint8_t *dstVal = (uint8_t*)dst; - - while (n--) - { - temp = *pdwVal; - pdwVal += FSDEV_PMA_STRIDE; - *dstVal++ = ((temp >> 0) & 0xFF); - *dstVal++ = ((temp >> 8) & 0xFF); - } - - if (wNBytes & 0x01) - { - temp = *pdwVal; - pdwVal += FSDEV_PMA_STRIDE; - *dstVal++ = ((temp >> 0) & 0xFF); - } - return true; -} -#endif - -/** - * @brief Copy a buffer from user packet memory area (PMA) to FIFO. - * Uses byte-access of system memory and 16-bit access of packet memory - * @param wNBytes no. of bytes to be copied. - * @retval None - */ -static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes) -{ + * @brief Copy a buffer from user packet memory area (PMA) to FIFO. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) { // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies // Check for first linear part tu_fifo_buffer_info_t info; - tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO + tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO - uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); - // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part, // last lin byte will be combined with wrapped part // To ensure PMA is always access aligned (src aligned to 16 or 32 bit) #ifdef FSDEV_BUS_32BIT - if((cnt_lin & 0x03) && cnt_wrap) - { + if ((cnt_lin & 0x03) && cnt_wrap) { // Copy first linear part - dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x03); - src += cnt_lin &~0x03; + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03); + src += cnt_lin & ~0x03; // Copy last linear bytes & first wrapped bytes uint8_t tmp[4]; @@ -1501,48 +1045,44 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB src += 4; uint32_t i; - for (i = 0; i < (cnt_lin & 0x03); i++) - { - ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i] = tmp[i]; + for (i = 0; i < (cnt_lin & 0x03); i++) { + ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i]; } uint32_t wCnt = cnt_wrap; - for (; i < 4 && wCnt > 0; i++, wCnt--) - { - *(uint8_t*)info.ptr_wrap = tmp[i]; - info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + *(uint8_t *)info.ptr_wrap = tmp[i]; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; } // Copy rest of wrapped byte - if (wCnt) + if (wCnt) { dcd_read_packet_memory(info.ptr_wrap, src, wCnt); + } } #else - if((cnt_lin & 0x01) && cnt_wrap) - { + if ((cnt_lin & 0x01) && cnt_wrap) { // Copy first linear part - dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x01); - src += cnt_lin &~0x01; + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01); + src += cnt_lin & ~0x01; // Copy last linear byte & first wrapped byte uint8_t tmp[2]; dcd_read_packet_memory(tmp, src, 2); src += 2; - ((uint8_t*)info.ptr_lin)[cnt_lin - 1] = tmp[0]; - ((uint8_t*)info.ptr_wrap)[0] = tmp[1]; + ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0]; + ((uint8_t *)info.ptr_wrap)[0] = tmp[1]; // Copy rest of wrapped byte - dcd_read_packet_memory(((uint8_t*)info.ptr_wrap) + 1, src, cnt_wrap - 1); + dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1); } #endif - else - { + else { // Copy linear part dcd_read_packet_memory(info.ptr_lin, src, cnt_lin); src += cnt_lin; - if(info.len_wrap) - { + if (info.len_wrap) { // Copy wrapped byte dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap); } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h deleted file mode 100644 index 946ad2c7c..000000000 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright(c) 2016 STMicroelectronics - * Copyright(c) N Conrad - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * This file is part of the TinyUSB stack. - */ - -// This file contains source copied from ST's HAL, and thus should have their copyright statement. - -// FSDEV_PMA_SIZE is PMA buffer size in bytes. -// On 512-byte devices, access with a stride of two words (use every other 16-bit address) -// On 1024-byte devices, access with a stride of one word (use every 16-bit address) - -#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ -#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ - -#if CFG_TUSB_MCU == OPT_MCU_STM32F0 - #include "stm32f0xx.h" - #define FSDEV_PMA_SIZE (1024u) - // F0x2 models are crystal-less - // All have internal D+ pull-up - // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support - // PMA dedicated to USB (no sharing with CAN) - -#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 - #include "stm32f1xx.h" - #define FSDEV_PMA_SIZE (512u) - // NO internal Pull-ups - // *B, and *C: 2 x 16 bits/word - - // F1 names this differently from the rest - #define USB_CNTR_LPMODE USB_CNTR_LP_MODE - -#elif defined(STM32F302xB) || defined(STM32F302xC) || \ - defined(STM32F303xB) || defined(STM32F303xC) || \ - defined(STM32F373xC) - #include "stm32f3xx.h" - #define FSDEV_PMA_SIZE (512u) - // NO internal Pull-ups - // *B, and *C: 1 x 16 bits/word - // PMA dedicated to USB (no sharing with CAN) - -#elif defined(STM32F302x6) || defined(STM32F302x8) || \ - defined(STM32F302xD) || defined(STM32F302xE) || \ - defined(STM32F303xD) || defined(STM32F303xE) - #include "stm32f3xx.h" - #define FSDEV_PMA_SIZE (1024u) - // NO internal Pull-ups - // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support - // When CAN clock is enabled, USB can use first 768 bytes ONLY. - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 - #include "stm32l0xx.h" - #define FSDEV_PMA_SIZE (1024u) - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 - #include "stm32l1xx.h" - #define FSDEV_PMA_SIZE (512u) - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 - #include "stm32g4xx.h" - #define FSDEV_PMA_SIZE (1024u) - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 - #include "stm32g0xx.h" - #define FSDEV_BUS_32BIT - #define FSDEV_PMA_SIZE (2048u) - #undef USB_PMAADDR - #define USB_PMAADDR USB_DRD_PMAADDR - #define USB_TypeDef USB_DRD_TypeDef - #define EP0R CHEP0R - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB USB_DRD_FS - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN - -#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 - #include "stm32h5xx.h" - #define FSDEV_BUS_32BIT - - #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) - #define USB_DRD_BASE USB_DRD_FS_BASE - #endif - - #define FSDEV_PMA_SIZE (2048u) - #undef USB_PMAADDR - #define USB_PMAADDR USB_DRD_PMAADDR - #define USB_TypeDef USB_DRD_TypeDef - #define EP0R CHEP0R - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB USB_DRD_FS - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN - -#elif CFG_TUSB_MCU == OPT_MCU_STM32WB - #include "stm32wbxx.h" - #define FSDEV_PMA_SIZE (1024u) - /* ST provided header has incorrect value */ - #undef USB_PMAADDR - #define USB_PMAADDR USB1_PMAADDR - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 - #include "stm32l4xx.h" - #define FSDEV_PMA_SIZE (1024u) - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 - #include "stm32l5xx.h" - #define FSDEV_PMA_SIZE (1024u) - - #ifndef USB_PMAADDR - #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) - #endif - -#else - #error You are using an untested or unimplemented STM32 variant. Please update the driver. - // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4 -#endif - -// For purposes of accessing the packet -#if ((FSDEV_PMA_SIZE) == 512u) - #define FSDEV_PMA_STRIDE (2u) -#elif ((FSDEV_PMA_SIZE) == 1024u) - #define FSDEV_PMA_STRIDE (1u) -#endif - -// The fsdev_bus_t type can be used for both register and PMA access necessities -// For type-safety create a new macro for the volatile address of PMAADDR -// The compiler should warn us if we cast it to a non-volatile type? -#ifdef FSDEV_BUS_32BIT -typedef uint32_t fsdev_bus_t; -static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR; - -#else -typedef uint16_t fsdev_bus_t; -// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden) -static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR; - -TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) { - size_t total_word_offset = (((USBx)->BTABLE)>>1) + x; - total_word_offset *= FSDEV_PMA_STRIDE; - return &(pma[total_word_offset]); -} - -TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { - return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u); -} - -TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { - return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u); -} -#endif - -/* Aligned buffer size according to hardware */ -TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) { - /* The STM32 full speed USB peripheral supports only a limited set of - * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ - uint16_t blocksize = (size > 62) ? 32 : 2; - - // Round up while dividing requested size by blocksize - uint16_t numblocks = (size + blocksize - 1) / blocksize ; - - return numblocks * blocksize; -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4); - *reg = wRegValue; -#else - __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u); - *reg = (uint16_t)wRegValue; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4); -#else - __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u); -#endif - return *reg; -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= (uint32_t)USB_EP_T_MASK; - regVal |= wType; - regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EP_T_FIELD; - return regVal; -} - -/** - * @brief Clears bit CTR_RX / CTR_TX in the endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal &= ~USB_EP_CTR_RX; - regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0) - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal &= ~USB_EP_CTR_TX; - regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0) - pcd_set_endpoint(USBx, bEpIdx,regVal); -} - -/** - * @brief gets counter of the tx buffer. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval Counter value - */ -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return (pma32[2*bEpIdx] & 0x03FF0000) >> 16; -#else - __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); - return *regPtr & 0x3ffU; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16; -#else - __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); - return *regPtr & 0x3ffU; -#endif -} - -/** - * @brief Sets address in an endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param bAddr Address. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= bAddr; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx,regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return pma32[2*bEpIdx] & 0x0000FFFFu ; -#else - return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - return pma32[2*bEpIdx + 1] & 0x0000FFFFu; -#else - return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu); -#else - *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu); -#else - *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); -#else - __IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); - *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); -#else - __IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); - *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, - uint32_t blocksize, uint32_t numblocks) { - /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ -#ifdef FSDEV_BUS_32BIT - (void) USBx; - pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26); -#else - __IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u); - *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10); -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) { - wCount = pcd_aligned_buffer_size(wCount); - - /* We assume that the buffer size is already aligned to hardware requirements. */ - uint16_t blocksize = (wCount > 62) ? 1 : 0; - uint16_t numblocks = wCount / (blocksize ? 32 : 2); - - /* There should be no remainder in the above calculation */ - TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/); - - /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ - pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { - pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { - pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); -} - -/** - * @brief sets the status for tx transfer (bits STAT_TX[1:0]). - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param wState new state - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPTX_DTOGMASK; - - /* toggle first bit ? */ - if((USB_EPTX_DTOG1 & (wState))!= 0U) - { - regVal ^= USB_EPTX_DTOG1; - } - /* toggle second bit ? */ - if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U) - { - regVal ^= USB_EPTX_DTOG2; - } - - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -/** - * @brief sets the status for rx transfer (bits STAT_TX[1:0]) - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @param wState new state - * @retval None - */ - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPRX_DTOGMASK; - - /* toggle first bit ? */ - if((USB_EPRX_DTOG1 & wState)!= 0U) { - regVal ^= USB_EPRX_DTOG1; - } - /* toggle second bit ? */ - if((USB_EPRX_DTOG2 & wState)!= 0U) { - regVal ^= USB_EPRX_DTOG2; - } - - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - return (regVal & USB_EPRX_STAT) >> (12u); -} - - -/** - * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -/** - * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - if((regVal & USB_EP_DTOG_RX) != 0) { - pcd_rx_dtog(USBx,bEpIdx); - } -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - if((regVal & USB_EP_DTOG_TX) != 0) { - pcd_tx_dtog(USBx,bEpIdx); - } -} - -/** - * @brief set & clear EP_KIND bit. - * @param USBx USB peripheral instance register address. - * @param bEpIdx Endpoint Number. - * @retval None - */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal |= USB_EP_KIND; - regVal &= USB_EPREG_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { - uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); - regVal &= USB_EPKIND_MASK; - regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; - pcd_set_endpoint(USBx, bEpIdx, regVal); -} - -// This checks if the device has "LPM" -#if defined(USB_ISTR_L1REQ) -#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) -#else -#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) -#endif - -#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ - USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) - -// Number of endpoints in hardware -// TODO should use TUP_DCD_ENDPOINT_MAX -#define STFSDEV_EP_COUNT (8u) - -#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */ diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h new file mode 100644 index 000000000..8c0961bb9 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h @@ -0,0 +1,220 @@ +/* +* The MIT License (MIT) + * + * Copyright (c) 2024, hathach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +/** <h2><center>© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.</center></h2> + * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + */ + +#ifndef TUSB_FSDEV_CH32_H +#define TUSB_FSDEV_CH32_H + +#include "common/tusb_compiler.h" + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + +#if CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include <ch32f20x.h> +#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X + #include <ch32v20x.h> +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#define FSDEV_PMA_SIZE (512u) +#define FSDEV_REG_BASE 0x40005C00UL + +#define USB_BASE (APB1PERIPH_BASE + 0x00005C00UL) /*!< USB_IP Peripheral Registers base address */ +#define USB_PMAADDR (APB1PERIPH_BASE + 0x00006000UL) /*!< USB_IP Packet Memory Area base address */ +#define USB ((USB_TypeDef *)USB_BASE) + +/******************************************************************************/ +/* */ +/* USB Device General registers */ +/* */ +/******************************************************************************/ +#define USB_CNTR (USB_BASE + 0x40U) /*!< Control register */ +#define USB_ISTR (USB_BASE + 0x44U) /*!< Interrupt status register */ +#define USB_FNR (USB_BASE + 0x48U) /*!< Frame number register */ +#define USB_DADDR (USB_BASE + 0x4CU) /*!< Device address register */ +#define USB_BTABLE (USB_BASE + 0x50U) /*!< Buffer Table address register */ + +/**************************** ISTR interrupt events *************************/ +#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ +#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ +#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */ +#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */ +#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */ +#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */ +#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */ +#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */ +#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */ +#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */ + +/* Legacy defines */ +#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR + +#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */ +#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/ +#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */ +#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */ +#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */ +#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */ +#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */ +#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */ + +/* Legacy defines */ +#define USB_CLR_PMAOVRM USB_CLR_PMAOVR + +/************************* CNTR control register bits definitions ***********/ +#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */ +#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */ +#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */ +#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */ +#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */ +#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */ +#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */ +#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */ +#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */ +#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */ +#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */ +#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */ +#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */ + +/* Legacy defines */ +#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR +#define USB_CNTR_LP_MODE USB_CNTR_LPMODE + +/******************** FNR Frame Number Register bit definitions ************/ +#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */ +#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */ +#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */ +#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */ +#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */ + +/******************** DADDR Device ADDRess bit definitions ****************/ +#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */ +#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */ + +/****************************** Endpoint register *************************/ +#define USB_EP0R USB_BASE /*!< endpoint 0 register address */ +#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */ +#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */ +#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */ +#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */ +#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */ +#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */ +#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */ +/* bit positions */ +#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */ +#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */ +#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */ +#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */ +#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */ +#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */ +#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */ +#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */ +#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */ +#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */ + +/* EndPoint REGister MASK (no toggle fields) */ +#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD) + /*!< EP_TYPE[1:0] EndPoint TYPE */ +#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */ +#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */ +#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */ +#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */ +#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */ +#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK) + +#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */ + /*!< STAT_TX[1:0] STATus for TX transfer */ +#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */ +#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */ +#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */ +#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */ +#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */ +#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */ +#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK) + /*!< STAT_RX[1:0] STATus for RX transfer */ +#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */ +#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */ +#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */ +#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */ +#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */ +#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */ +#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK) + + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +#if CFG_TUSB_MCU == OPT_MCU_CH32V20X +static const IRQn_Type fsdev_irq[] = { + USB_HP_CAN1_TX_IRQn, + USB_LP_CAN1_RX0_IRQn, + USBWakeUp_IRQn +}; +enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; +#else + #error "Unsupported MCU" +#endif + +void dcd_int_enable(uint8_t rhport) { + (void)rhport; + for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { + NVIC_EnableIRQ(fsdev_irq[i]); + } +} + +void dcd_int_disable(uint8_t rhport) { + (void)rhport; + for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { + NVIC_DisableIRQ(fsdev_irq[i]); + } +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN; +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN; +} + +#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h new file mode 100644 index 000000000..bb2c72fd1 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -0,0 +1,334 @@ +/* + * The MIT License (MIT) + * + * Copyright(c) N Conrad + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_FSDEV_STM32_H +#define TUSB_FSDEV_STM32_H + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 + #include "stm32f0xx.h" + #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_REG_BASE USB_BASE + // F0x2 models are crystal-less + // All have internal D+ pull-up + // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support + // PMA dedicated to USB (no sharing with CAN) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 2 x 16 bits/word + + // F1 names this differently from the rest + #define USB_CNTR_LPMODE USB_CNTR_LP_MODE + +#elif defined(STM32F302xB) || defined(STM32F302xC) || \ + defined(STM32F303xB) || defined(STM32F303xC) || \ + defined(STM32F373xC) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 1 x 16 bits/word + // PMA dedicated to USB (no sharing with CAN) + +#elif defined(STM32F302x6) || defined(STM32F302x8) || \ + defined(STM32F302xD) || defined(STM32F302xE) || \ + defined(STM32F303xD) || defined(STM32F303xE) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (1024u) + // NO internal Pull-ups + // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support + // When CAN clock is enabled, USB can use first 768 bytes ONLY. + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 + #include "stm32l0xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + #include "stm32l1xx.h" + #define FSDEV_PMA_SIZE (512u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + #include "stm32g4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #include "stm32g0xx.h" + #define FSDEV_BUS_32BIT + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + #include "stm32h5xx.h" + #define FSDEV_BUS_32BIT + + #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) + #define USB_DRD_BASE USB_DRD_FS_BASE + #endif + + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + #include "stm32wbxx.h" + #define FSDEV_PMA_SIZE (1024u) + /* ST provided header has incorrect value */ + #undef USB_PMAADDR + #define USB_PMAADDR USB1_PMAADDR + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + #include "stm32l5xx.h" + #define FSDEV_PMA_SIZE (1024u) + + #ifndef USB_PMAADDR + #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) + #endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #include "stm32u5xx.h" + #define FSDEV_BUS_32BIT + + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#else + #error You are using an untested or unimplemented STM32 variant. Please update the driver. + // This includes U0 +#endif + +#if defined(USB_BASE) + #define FSDEV_REG_BASE USB_BASE +#elif defined(USB_DRD_BASE) + #define FSDEV_REG_BASE USB_DRD_BASE +#elif defined(USB_DRD_FS_BASE) + #define FSDEV_REG_BASE USB_DRD_FS_BASE +#else + #error "FSDEV_REG_BASE not defined" +#endif + +// This checks if the device has "LPM" +#if defined(USB_ISTR_L1REQ) +#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) +#else +#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) +#endif + +#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ + USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn) + #define USBWakeUp_IRQn USB_FS_WKUP_IRQn +#endif + +static const IRQn_Type fsdev_irq[] = { + #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4) + USB_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + USB_HP_CAN1_TX_IRQn, + USB_LP_CAN1_RX0_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F3 + // USB remap handles dcd functions + USB_HP_CAN_TX_IRQn, + USB_LP_CAN_RX0_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #ifdef STM32G0B0xx + USB_IRQn, + #else + USB_UCPD1_2_IRQn, + #endif + #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1) + USB_HP_IRQn, + USB_LP_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + USB_DRD_FS_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + USB_FS_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32WB + USB_HP_IRQn, + USB_LP_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + USB_IRQn, + #else + #error Unknown arch in USB driver + #endif +}; +enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; + +void dcd_int_enable(uint8_t rhport) { + (void)rhport; + + // forces write to RAM before allowing ISR to execute + __DSB(); __ISB(); + + #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP) + // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from + // shared USB/CAN IRQs to separate CAN and USB IRQs. + // This dynamically checks if this remap is active to enable the right IRQs. + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + } else + #endif + { + for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) { + NVIC_EnableIRQ(fsdev_irq[i]); + } + } +} + +void dcd_int_disable(uint8_t rhport) { + (void)rhport; + + #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP) + // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from + // shared USB/CAN IRQs to separate CAN and USB IRQs. + // This dynamically checks if this remap is active to enable the right IRQs. + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + } else + #endif + { + for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) { + NVIC_DisableIRQ(fsdev_irq[i]); + } + } + + // CMSIS has a membar after disabling interrupts +} + +// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. +#if defined(USB_BCDR_DPPU) + +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + USB->BCDR &= ~(USB_BCDR_DPPU); +} + +void dcd_connect(uint8_t rhport) { + (void)rhport; + USB->BCDR |= USB_BCDR_DPPU; +} + +#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 + +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); +} + +void dcd_connect(uint8_t rhport) { + (void)rhport; + SYSCFG->PMC |= SYSCFG_PMC_USB_PU; +} +#endif + + +#endif /* TUSB_FSDEV_STM32_H */ diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h new file mode 100644 index 000000000..e4a0f3f28 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_type.h @@ -0,0 +1,282 @@ +/* + * The MIT License (MIT) + * + * Copyright(c) 2016 STMicroelectronics + * Copyright(c) N Conrad + * Copyright (c) 2024, hathach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef TUSB_FSDEV_TYPE_H +#define TUSB_FSDEV_TYPE_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "stdint.h" + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef FSDEV_BTABLE_BASE +#define FSDEV_BTABLE_BASE 0U +#endif + +TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes"); + +// FSDEV_PMA_SIZE is PMA buffer size in bytes. +// - 512-byte devices, access with a stride of two words (use every other 16-bit address) +// - 1024-byte devices, access with a stride of one word (use every 16-bit address) +// - 2048-byte devices, access with 32-bit address + +// For purposes of accessing the packet +#if FSDEV_PMA_SIZE == 512 + #define FSDEV_PMA_STRIDE (2u) // 1x16 bit access scheme + #define pma_aligned TU_ATTR_ALIGNED(4) +#elif FSDEV_PMA_SIZE == 1024 + #define FSDEV_PMA_STRIDE (1u) // 2x16 bit access scheme + #define pma_aligned +#elif FSDEV_PMA_SIZE == 2048 + #ifndef FSDEV_BUS_32BIT + #warning "FSDEV_PMA_SIZE is 2048, but FSDEV_BUS_32BIT is not defined" + #endif + #define FSDEV_PMA_STRIDE (1u) // 32 bit access scheme + #define pma_aligned +#endif + +//--------------------------------------------------------------------+ +// BTable Typedef +//--------------------------------------------------------------------+ +enum { + BTABLE_BUF_TX = 0, + BTABLE_BUF_RX = 1 +}; + +// hardware limit endpoint +#define FSDEV_EP_COUNT 8 + +// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either +// 16-bit or 32-bit depending on FSDEV_BUS_32BIT. +typedef union { + // 0: TX (IN), 1: RX (OUT) + + // strictly 16-bit access (could be 32-bit aligned) + struct { + volatile pma_aligned uint16_t addr; + volatile pma_aligned uint16_t count; + } ep16[FSDEV_EP_COUNT][2]; + + // strictly 32-bit access + struct { + volatile uint32_t count_addr; + } ep32[FSDEV_EP_COUNT][2]; +} fsdev_btable_t; + +TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct"); +TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM"); + +#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (USB_PMAADDR+FSDEV_BTABLE_BASE)) + +typedef struct { + volatile pma_aligned uint16_t u16; +} fsdev_pma16_t; + +//--------------------------------------------------------------------+ +// Registers Typedef +//--------------------------------------------------------------------+ + +// volatile 32-bit aligned +#define _va32 volatile TU_ATTR_ALIGNED(4) + +// The fsdev_bus_t type can be used for both register and PMA access necessities +#ifdef FSDEV_BUS_32BIT +typedef uint32_t fsdev_bus_t; +#else +typedef uint16_t fsdev_bus_t; +#endif + +typedef struct { + struct { + _va32 fsdev_bus_t reg; + }ep[FSDEV_EP_COUNT]; + + _va32 uint32_t RESERVED7[8]; // Reserved + _va32 fsdev_bus_t CNTR; // 40: Control register + _va32 fsdev_bus_t ISTR; // 44: Interrupt status register + _va32 fsdev_bus_t FNR; // 48: Frame number register + _va32 fsdev_bus_t DADDR; // 4C: Device address register + _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only) + _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only) + _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only) +} fsdev_regs_t; + +TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset"); +TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct"); + +#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE) + + +#ifndef USB_EPTX_STAT +#define USB_EPTX_STAT 0x0030U +#endif + +#ifndef USB_EPRX_STAT +#define USB_EPRX_STAT 0x3000U +#endif + +#ifndef USB_EPTX_STAT_Pos +#define USB_EPTX_STAT_Pos 4u +#endif + +#ifndef USB_EP_DTOG_TX_Pos +#define USB_EP_DTOG_TX_Pos 6u +#endif + +#ifndef USB_EP_CTR_TX_Pos +#define USB_EP_CTR_TX_Pos 7u +#endif + + +#define EP_CTR_TXRX (USB_EP_CTR_TX | USB_EP_CTR_RX) + +typedef enum { + EP_STAT_DISABLED = 0, + EP_STAT_STALL = 1, + EP_STAT_NAK = 2, + EP_STAT_VALID = 3 +}ep_stat_t; + +#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) +#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) + +//--------------------------------------------------------------------+ +// Endpoint Helper +// - CTR is write 0 to clear +// - DTOG and STAT are write 1 to toggle +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value) { + FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) { + return FSDEV_REG->ep[ep_id].reg; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_status(uint32_t reg, tusb_dir_t dir, ep_stat_t state) { + return reg ^ (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_dtog(uint32_t reg, tusb_dir_t dir, uint8_t state) { + return reg ^ (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_clear_ctr(uint32_t reg, tusb_dir_t dir) { + return reg & ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) { + return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; +} + +//--------------------------------------------------------------------+ +// BTable Helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) { +#ifdef FSDEV_BUS_32BIT + return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu; +#else + return FSDEV_BTABLE->ep16[ep_id][buf_id].addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) { +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_count(uint32_t ep_id, uint8_t buf_id) { + uint16_t count; +#ifdef FSDEV_BUS_32BIT + count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16); +#else + count = FSDEV_BTABLE->ep16[ep_id][buf_id].count; +#endif + return count & 0x3FFU; +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) { +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count; + cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU); + FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt; +#endif +} + +/* Aligned buffer size according to hardware */ +TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t block_in_bytes; + if (size > 62) { + block_in_bytes = 32; + *blsize = 1; + } else { + block_in_bytes = 2; + *blsize = 0; + } + + *num_block = tu_div_ceil(size, block_in_bytes); + + return (*num_block) * block_in_bytes; +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint32_t wCount) { + uint8_t blsize, num_block; + (void) pma_align_buffer_size(wCount, &blsize, &num_block); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10); + +#ifdef FSDEV_BUS_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb; +#endif +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index 6cc1975a8..6f36ad441 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -35,7 +35,9 @@ #include <f1c100s-irq.h> #include <device/dcd.h> #include "musb_def.h" -#include "bsp/board.h" + +//#include "bsp/board_api.h" +extern uint32_t board_millis(void); // TODO remove typedef uint32_t u32; typedef uint16_t u16; @@ -58,7 +60,7 @@ typedef struct TU_ATTR_PACKED typedef struct { - tusb_control_request_t setup_packet; + CFG_TUD_MEM_ALIGN tusb_control_request_t setup_packet; uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */ int8_t status_out; pipe_state_t pipe0; @@ -350,7 +352,7 @@ static void USBC_INT_DisableRxEp(u8 ep_index) * INTERNAL FUNCTION DECLARATION *------------------------------------------------------------------*/ -static dcd_data_t _dcd; +CFG_TUD_MEM_ALIGN static dcd_data_t _dcd; static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr) { @@ -560,7 +562,7 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne static void process_setup_packet(uint8_t rhport) { - uint32_t *p = (uint32_t*)&_dcd.setup_packet; + uint32_t *p = (uint32_t*)(uintptr_t) &_dcd.setup_packet; p[0] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE)); p[1] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE)); @@ -594,7 +596,7 @@ static bool handle_xfer_in(uint_fast8_t ep_addr) if (len) { volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2)); if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) { - pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_IN); + pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t) buf, addr, len, TUSB_DIR_IN); } else { pipe_write_packet(buf, addr, len); pipe->buf = buf + len; @@ -622,7 +624,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr) if (len) { volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2)); if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) { - pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_OUT); + pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t )buf, addr, len, TUSB_DIR_OUT); } else { pipe_read_packet(buf, addr, len); pipe->buf = buf + len; diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 8b13b0753..9a38b46dc 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -112,6 +112,17 @@ static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) { return 15 + 2 * (max_ep_size / 4) + 2 * ep_count; } +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { + // flush TX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos); + while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} +} +TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) { + // flush RX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_RXFFLSH; + while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} +} + static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const ep_count = _dwc2_controller[rhport].ep_count; @@ -120,7 +131,7 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { TU_ASSERT(epnum < ep_count); - uint16_t const fifo_size = tu_div_ceil(packet_size, 4); + uint16_t fifo_size = tu_div_ceil(packet_size, 4); // "USB Data FIFOs" section in reference manual // Peripheral FIFO architecture @@ -154,10 +165,16 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { dwc2->grxfsiz = sz; } } else { + // Note if The TXFELVL is configured as half empty. In order + // to be able to write a packet at that point, the fifo must be twice the max_size. + if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) { + fifo_size *= 2; + } + // Check if free space is available TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4); _allocated_fifo_words_tx += fifo_size; - TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size * 4, + TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4, _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); // DIEPTXF starts at FIFO #1. @@ -185,10 +202,10 @@ static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoin (xfer->max_size << DOEPCTL_MPSIZ_Pos); if (dir == TUSB_DIR_OUT) { - dwc2->epout[epnum].doepctl |= dxepctl; + dwc2->epout[epnum].doepctl = dxepctl; dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); } else { - dwc2->epin[epnum].diepctl |= dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); + dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); } } @@ -219,8 +236,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { } // Flush the FIFO, and wait until we have confirmed it cleared. - dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH); - while ((dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0) {} + fifo_flush_tx(dwc2, epnum); } else { dwc2_epout_t* epout = dwc2->epout; @@ -264,15 +280,17 @@ static void bus_reset(uint8_t rhport) { dwc2->epout[n].doepctl |= DOEPCTL_SNAK; } - // flush all TX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos); - while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} + // 2. Disable all IN endpoints + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + } - // flush RX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_RXFFLSH; - while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); - // 2. Set up interrupt mask + // 3. Set up interrupt mask dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; @@ -408,9 +426,9 @@ void print_dwc2_info(dwc2_regs_t* dwc2) { volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid; TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n"); for (size_t i = 0; i < 5; i++) { - TU_LOG(DWC2_DEBUG, "0x%08lX, ", p[i]); + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]); } - TU_LOG(DWC2_DEBUG, "0x%08lX\r\n", p[5]); + TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]); } #endif @@ -429,11 +447,15 @@ static void reset_core(dwc2_regs_t* dwc2) { } static bool phy_hs_supported(dwc2_regs_t* dwc2) { - // note: esp32 incorrect report its hs_phy_type as utmi + (void) dwc2; + #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // note: esp32 incorrect report its hs_phy_type as utmi + return false; +#elif !TUD_OPT_HIGH_SPEED return false; #else - return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE; + return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE; #endif } @@ -578,13 +600,8 @@ void dcd_init(uint8_t rhport) { // (non zero-length packet), send STALL back and discard. dwc2->dcfg |= DCFG_NZLSOHSK; - // flush all TX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos); - while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} - - // flush RX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_RXFFLSH; - while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); // Clear all interrupts uint32_t int_mask = dwc2->gintsts; @@ -596,6 +613,9 @@ void dcd_init(uint8_t rhport) { dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + // Configure TX FIFO empty level for interrupt. Default is complete empty + dwc2->gahbcfg |= GAHBCFG_TXFELVL; + // Enable global interrupt dwc2->gahbcfg |= GAHBCFG_GINT; @@ -691,16 +711,25 @@ void dcd_edpt_close_all(uint8_t rhport) { for (uint8_t n = 1; n < ep_count; n++) { // disable OUT endpoint - dwc2->epout[n].doepctl = 0; + if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS; + } xfer_status[n][TUSB_DIR_OUT].max_size = 0; // disable IN endpoint - dwc2->epin[n].diepctl = 0; + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } xfer_status[n][TUSB_DIR_IN].max_size = 0; } + // reset allocated fifo OUT + dwc2->grxfsiz = calc_grxfsiz(64, ep_count); // reset allocated fifo IN _allocated_fifo_words_tx = 16; + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { @@ -1122,16 +1151,14 @@ void dcd_int_handler(uint8_t rhport) { if(int_status & GINTSTS_SOF) { dwc2->gintsts = GINTSTS_SOF; + const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; - if (_sof_en) { - uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8; - dcd_event_sof(rhport, frame, true); - } else { - // Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection + // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection + if (!_sof_en) { dwc2->gintmsk &= ~GINTMSK_SOFM; } - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); + dcd_event_sof(rhport, frame, true); } // RxFIFO non-empty interrupt handling. @@ -1144,7 +1171,7 @@ void dcd_int_handler(uint8_t rhport) { // Loop until all available packets were handled do { handle_rxflvl_irq(rhport); - } while (dwc2->gotgint & GINTSTS_RXFLVL); + } while(dwc2->gintsts & GINTSTS_RXFLVL); dwc2->gintmsk |= GINTMSK_RXFLVLM; } @@ -1168,4 +1195,13 @@ void dcd_int_handler(uint8_t rhport) { // } } +#if CFG_TUD_TEST_MODE +void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Enable the test mode + dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (((uint8_t) test_selector) << DCTL_TCTL_Pos); +} +#endif + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index c50dd66b8..932f52f40 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -35,6 +35,7 @@ #include "esp_intr_alloc.h" #include "soc/periph_defs.h" //#include "soc/usb_periph.h" +#include "freertos/task.h" #define DWC2_REG_BASE 0x60080000UL #define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN) diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index c15771237..cb694b326 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -1,17 +1,34 @@ -/** - * @author MCD Application Team - * Ha Thach (tinyusb.org) - * - * @attention - * - * <h2><center>© Copyright (c) 2019 STMicroelectronics. +/* + * The MIT License (MIT) + * + * Copyright (c) 2024, hathach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +/** <h2><center>© Copyright (c) 2019 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause - * */ #ifndef _TUSB_DWC2_TYPES_H_ diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c index b4dfda575..8005f5f7b 100644 --- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c +++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c @@ -94,7 +94,8 @@ static void bus_reset(void) USBOEPCNT_0 &= ~NAK; USBIEPCNT_0 &= ~NAK; - USBCTL |= FEN; // Enable responding to packets. + // Enable responding to packets. + USBCTL |= FEN; // Dedicated buffers in hardware for SETUP and EP0, no setup needed. // Now safe to respond to SETUP packets. @@ -103,6 +104,28 @@ static void bus_reset(void) USBKEYPID = 0; } +// Controls reset behavior of the USB module on receipt of a bus reset event. +// - enable: When true, bus reset events will cause a reset the USB module. +static void enable_functional_reset(const bool enable) +{ + // Check whether or not the USB configuration registers were + // locked prior to this function being called so that, if + // necessary, the lock state can be restored on exit. + bool unlocked = (USBKEYPID == 0xA528) ? true : false; + + if(!unlocked) USBKEYPID = USBKEY; + + if(enable) + { + USBCTL |= FRSTE; + } + else + { + USBCTL &= ~FRSTE; + } + + if(!unlocked) USBKEYPID = 0; +} /*------------------------------------------------------------------*/ /* Controller API @@ -131,11 +154,14 @@ void dcd_init (uint8_t rhport) USBVECINT = 0; - // Enable reset and wait for it before continuing. - USBIE |= RSTRIE; - - // Enable pullup. - USBCNF |= PUR_EN; + if(USBPWRCTL & USBBGVBV) {// Bus power detected? + USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt. + USBIE |= RSTRIE; // Enable reset and wait for it before continuing. + USBCNF |= PUR_EN; // Enable pullup. + } else { + USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt. + USBCNF &= ~USB_EN; // Disable USB module until bus power is detected. + } USBKEYPID = 0; } @@ -610,14 +636,76 @@ static void handle_setup_packet(void) _setup_packet[i] = setup_buf[i]; } - // Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we - // have a SETUP packet, force NAKs until tinyusb can handle the SETUP - // packet and prepare for a new xfer. + // Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer. USBIEPCNT_0 |= NAK; USBOEPCNT_0 |= NAK; + + // Clear SETUPIFG to avoid handling in the USBVECINT switch statement. + // When handled there the NAKs applied to the endpoints above are + // cleared by hardware and the host will receive stale/duplicate data. + // + // Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide: + // + // "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on + // input endpoint 0 and output endpoint 0 is also cleared." + USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround. + USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround. + USBIFG &= ~SETUPIFG; + USBIEPCNF_0 |= UBME; // Errata USB10 workaround. + USBOEPCNF_0 |= UBME; // Errata USB10 workaround. dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true); } +#if CFG_TUSB_OS == OPT_OS_NONE +TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) { + // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles + // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop + volatile uint32_t cycles = (25000 * ms) >> 2; + while (cycles > 0) { + cycles--; + asm("nop"); + } +} +#else +#define tu_delay(ms) osal_task_delay(ms) +#endif + +static void handle_bus_power_event(void *param) { + (void) param; + + tu_delay(5); // Bus power settling delay. + + USBKEYPID = USBKEY; + + if(USBPWRCTL & USBBGVBV) { // Event caused by application of bus power. + USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt. + USBPLLDIVB = USBPLLDIVB; // For some reason the PLL will *NOT* lock unless the divider + // register is re-written. The assumption here is that this + // register was already properly configured during board-level + // initialization. + USBPLLCTL |= (UPLLEN | UPFDEN); // Enable the PLL. + + uint16_t attempts = 0; + do { // Poll the PLL, checking for a successful lock. + USBPLLIR = 0; + tu_delay(1); + attempts++; + } while ((attempts < 10) && (USBPLLIR != 0)); + + // A successful lock is indicated by all PLL-related interrupt flags being cleared. + if(!USBPLLIR) { + dcd_init(0); // Re-initialize the USB module. + } + } else { // Event caused by removal of bus power. + USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt. + USBPLLCTL &= ~(UPLLEN | UPFDEN); // Disable the PLL. + USBCNF = 0; // Disable the USB module. + dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false); + } + + USBKEYPID = 0; +} + void dcd_int_handler(uint8_t rhport) { (void) rhport; @@ -628,6 +716,7 @@ void dcd_int_handler(uint8_t rhport) if(setup_status) { + enable_functional_reset(true); handle_setup_packet(); } @@ -646,11 +735,32 @@ void dcd_int_handler(uint8_t rhport) switch(curr_vector) { + case USBVECINT_NONE: + break; + case USBVECINT_RSTR: + enable_functional_reset(false); // Errata USB4 workaround. bus_reset(); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); break; + case USBVECINT_PWR_VBUSOn: + case USBVECINT_PWR_VBUSOff: { + USBKEYPID = USBKEY; + // Prevent (possibly) unstable power from generating spurious interrupts. + USBPWRCTL &= ~(VBONIE | VBOFFIE); + USBKEYPID = 0; + + dcd_event_t event; + + event.rhport = 0; + event.event_id = USBD_EVENT_FUNC_CALL; + event.func_call.func = handle_bus_power_event; + + dcd_event_handler(&event, true); + } + break; + // Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet // is received. At this point, even though the hardware is no longer // forcing NAKs, the EP0 NAK bits should still be set to avoid @@ -675,10 +785,12 @@ void dcd_int_handler(uint8_t rhport) break; case USBVECINT_INPUT_ENDPOINT0: + enable_functional_reset(true); transmit_packet(0); break; case USBVECINT_OUTPUT_ENDPOINT0: + enable_functional_reset(true); receive_packet(0); break; @@ -710,7 +822,6 @@ void dcd_int_handler(uint8_t rhport) default: while(true); - break; } } diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h new file mode 100644 index 000000000..68be64f5e --- /dev/null +++ b/src/portable/wch/ch32_usbfs_reg.h @@ -0,0 +1,175 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef USB_CH32_USBFS_REG_H +#define USB_CH32_USBFS_REG_H + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + +#if CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include <ch32f20x.h> +#elif CFG_TUSB_MCU == OPT_MCU_CH32V103 + #include <ch32v10x.h> + typedef struct + { + __IO uint8_t BASE_CTRL; + __IO uint8_t UDEV_CTRL; + __IO uint8_t INT_EN; + __IO uint8_t DEV_ADDR; + __IO uint8_t Reserve0; + __IO uint8_t MIS_ST; + __IO uint8_t INT_FG; + __IO uint8_t INT_ST; + __IO uint32_t RX_LEN; + __IO uint8_t UEP4_1_MOD; + __IO uint8_t UEP2_3_MOD; + __IO uint8_t UEP5_6_MOD; + __IO uint8_t UEP7_MOD; + __IO uint32_t UEP0_DMA; + __IO uint32_t UEP1_DMA; + __IO uint32_t UEP2_DMA; + __IO uint32_t UEP3_DMA; + __IO uint32_t UEP4_DMA; + __IO uint32_t UEP5_DMA; + __IO uint32_t UEP6_DMA; + __IO uint32_t UEP7_DMA; + __IO uint16_t UEP0_TX_LEN; + __IO uint8_t UEP0_TX_CTRL; + __IO uint8_t UEP0_RX_CTRL; + __IO uint16_t UEP1_TX_LEN; + __IO uint8_t UEP1_TX_CTRL; + __IO uint8_t UEP1_RX_CTRL; + __IO uint16_t UEP2_TX_LEN; + __IO uint8_t UEP2_TX_CTRL; + __IO uint8_t UEP2_RX_CTRL; + __IO uint16_t UEP3_TX_LEN; + __IO uint8_t UEP3_TX_CTRL; + __IO uint8_t UEP3_RX_CTRL; + __IO uint16_t UEP4_TX_LEN; + __IO uint8_t UEP4_TX_CTRL; + __IO uint8_t UEP4_RX_CTRL; + __IO uint16_t UEP5_TX_LEN; + __IO uint8_t UEP5_TX_CTRL; + __IO uint8_t UEP5_RX_CTRL; + __IO uint16_t UEP6_TX_LEN; + __IO uint8_t UEP6_TX_CTRL; + __IO uint8_t UEP6_RX_CTRL; + __IO uint16_t UEP7_TX_LEN; + __IO uint8_t UEP7_TX_CTRL; + __IO uint8_t UEP7_RX_CTRL; + __IO uint32_t Reserve1; + __IO uint32_t OTG_CR; + __IO uint32_t OTG_SR; + } USBOTG_FS_TypeDef; + + #define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400) +#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X + #include <ch32v20x.h> +#elif CFG_TUSB_MCU == OPT_MCU_CH32V307 + #include <ch32v30x.h> + #define USBHD_IRQn OTG_FS_IRQn +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// CTRL +#define USBFS_CTRL_DMA_EN (1 << 0) +#define USBFS_CTRL_CLR_ALL (1 << 1) +#define USBFS_CTRL_RESET_SIE (1 << 2) +#define USBFS_CTRL_INT_BUSY (1 << 3) +#define USBFS_CTRL_SYS_CTRL (1 << 4) +#define USBFS_CTRL_DEV_PUEN (1 << 5) +#define USBFS_CTRL_LOW_SPEED (1 << 6) +#define USBFS_CTRL_HOST_MODE (1 << 7) + +// INT_EN +#define USBFS_INT_EN_BUS_RST (1 << 0) +#define USBFS_INT_EN_DETECT (1 << 0) +#define USBFS_INT_EN_TRANSFER (1 << 1) +#define USBFS_INT_EN_SUSPEND (1 << 2) +#define USBFS_INT_EN_HST_SOF (1 << 3) +#define USBFS_INT_EN_FIFO_OV (1 << 4) +#define USBFS_INT_EN_DEV_NAK (1 << 6) +#define USBFS_INT_EN_DEV_SOF (1 << 7) + +// INT_FG +#define USBFS_INT_FG_BUS_RST (1 << 0) +#define USBFS_INT_FG_DETECT (1 << 0) +#define USBFS_INT_FG_TRANSFER (1 << 1) +#define USBFS_INT_FG_SUSPEND (1 << 2) +#define USBFS_INT_FG_HST_SOF (1 << 3) +#define USBFS_INT_FG_FIFO_OV (1 << 4) +#define USBFS_INT_FG_SIE_FREE (1 << 5) +#define USBFS_INT_FG_TOG_OK (1 << 6) +#define USBFS_INT_FG_IS_NAK (1 << 7) + +// INT_ST +#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F) +#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03) + +// UDEV_CTRL +#define USBFS_UDEV_CTRL_PORT_EN (1 << 0) +#define USBFS_UDEV_CTRL_GP_BIT (1 << 1) +#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2) +#define USBFS_UDEV_CTRL_DM_PIN (1 << 4) +#define USBFS_UDEV_CTRL_DP_PIN (1 << 5) +#define USBFS_UDEV_CTRL_PD_DIS (1 << 7) + +// TX_CTRL +#define USBFS_EP_T_RES_MASK (3 << 0) +#define USBFS_EP_T_TOG (1 << 2) +#define USBFS_EP_T_AUTO_TOG (1 << 3) + +#define USBFS_EP_T_RES_ACK (0 << 0) +#define USBFS_EP_T_RES_NYET (1 << 0) +#define USBFS_EP_T_RES_NAK (2 << 0) +#define USBFS_EP_T_RES_STALL (3 << 0) + +// RX_CTRL +#define USBFS_EP_R_RES_MASK (3 << 0) +#define USBFS_EP_R_TOG (1 << 2) +#define USBFS_EP_R_AUTO_TOG (1 << 3) + +#define USBFS_EP_R_RES_ACK (0 << 0) +#define USBFS_EP_R_RES_NYET (1 << 0) +#define USBFS_EP_R_RES_NAK (2 << 0) +#define USBFS_EP_R_RES_STALL (3 << 0) + +// token PID +#define PID_OUT 0 +#define PID_SOF 1 +#define PID_IN 2 +#define PID_SETUP 3 + +#endif // USB_CH32_USBFS_REG_H diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h index 9b956231f..87300b497 100644 --- a/src/portable/wch/ch32_usbhs_reg.h +++ b/src/portable/wch/ch32_usbhs_reg.h @@ -1,12 +1,51 @@ -#ifndef _USB_CH32_USBHS_REG_H -#define _USB_CH32_USBHS_REG_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * 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. + */ -#if (CFG_TUSB_MCU == OPT_MCU_CH32V307) -#include <ch32v30x.h> -#elif (CFG_TUSB_MCU == OPT_MCU_CH32F20X) -#include <ch32f20x.h> +#ifndef USB_CH32_USBHS_REG_H +#define USB_CH32_USBHS_REG_H + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + +#if CFG_TUSB_MCU == OPT_MCU_CH32V307 + #include <ch32v30x.h> +#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include <ch32f20x.h> +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop #endif + /******************* GLOBAL ******************/ // USB CONTROL @@ -36,8 +75,13 @@ // USB DEV AD #define USBHS_DEV_AD_OFFSET 0x03 + // USB FRAME_NO #define USBHS_FRAME_NO_OFFSET 0x04 +#define USBHS_FRAME_NO_NUM_MASK (0x7FF) +#define USBHS_FRAME_NO_MICROFRAME_SHIFT (11) +#define USBHS_FRAME_NO_MICROFRAME_MASK (0x7 << USBHS_FRAME_NO_MICROFRAME_SHIFT) + // USB SUSPEND #define USBHS_SUSPEND_OFFSET 0x06 #define USBHS_DEV_REMOTE_WAKEUP (1 << 2) @@ -47,7 +91,10 @@ // USB SPEED TYPE #define USBHS_SPEED_TYPE_OFFSET 0x08 -#define USBSPEED_MASK (0x03) +#define USBHS_SPEED_TYPE_MASK 0x03 +#define USBHS_SPEED_TYPE_FULL 0 +#define USBHS_SPEED_TYPE_HIGH 1 +#define USBHS_SPEED_TYPE_LOW 2 // USB_MIS_ST #define USBHS_MIS_ST_OFFSET 0x09 @@ -72,12 +119,16 @@ #define USBHS_ISO_ACT_FLAG (1 << 6) // INT_ST -#define USBHS_INT_ST_OFFSET 0x0B -#define USBHS_DEV_UIS_IS_NAK (1 << 7) -#define USBHS_DEV_UIS_TOG_OK (1 << 6) -#define MASK_UIS_TOKEN (3 << 4) -#define MASK_UIS_ENDP (0x0F) -#define MASK_UIS_H_RES (0x0F) +#define USBHS_INT_ST_OFFSET 0x0B +#define USBHS_DEV_UIS_IS_NAK (1 << 7) +#define USBHS_DEV_UIS_TOG_OK (1 << 6) +#define MASK_UIS_TOKEN (3 << 4) +#define USBHS_TOKEN_PID_OUT (0 << 4) +#define USBHS_TOKEN_PID_SOF (1 << 4) +#define USBHS_TOKEN_PID_IN (2 << 4) +#define USBHS_TOKEN_PID_SETUP (3 << 4) +#define MASK_UIS_ENDP (0x0F) +#define MASK_UIS_H_RES (0x0F) #define USBHS_TOGGLE_OK (0x40) #define USBHS_HOST_RES (0x0f) @@ -340,10 +391,5 @@ #define USBHS_UH_T_TOG_AUTO (1 << 5) #define USBHS_UH_T_DATA_NO (1 << 6) -// 00: OUT, 01:SOF, 10:IN, 11:SETUP -#define PID_OUT 0 -#define PID_SOF 1 -#define PID_IN 2 -#define PID_SETUP 3 #endif diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c new file mode 100644 index 000000000..9ed370b40 --- /dev/null +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -0,0 +1,344 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS + +#include "device/dcd.h" +#include "ch32_usbfs_reg.h" + +/* private defines */ +#define EP_MAX (8) + +#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep]) +#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep]) +#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep]) +#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep]) + +/* private data */ +struct usb_xfer { + bool valid; + uint8_t* buffer; + size_t len; + size_t processed_len; + size_t max_size; +}; + +static struct { + bool ep0_tog; + bool isochronous[EP_MAX]; + struct usb_xfer xfer[EP_MAX][2]; + TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64]; + TU_ATTR_ALIGNED(4) struct { + // OUT transfers >64 bytes will overwrite queued IN data! + uint8_t out[64]; + uint8_t in[1023]; + uint8_t pad; + } ep3_buffer; +} data; + +/* private helpers */ +static void update_in(uint8_t rhport, uint8_t ep, bool force) { + struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN]; + if (xfer->valid) { + if (force || xfer->len) { + size_t len = TU_MIN(xfer->max_size, xfer->len); + if (ep == 0) { + memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk + } else if (ep == 3) { + memcpy(data.ep3_buffer.in, xfer->buffer, len); + } else { + memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len); + } + xfer->buffer += len; + xfer->len -= len; + xfer->processed_len += len; + + EP_TX_LEN(ep) = len; + if (ep == 0) { + EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0); + data.ep0_tog = !data.ep0_tog; + } else if (data.isochronous[ep]) { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK; + } + } else { + xfer->valid = false; + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK; + dcd_event_xfer_complete( + rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len, + XFER_RESULT_SUCCESS, true); + } + } +} + +static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) { + struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT]; + if (xfer->valid) { + size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len)); + if (ep == 3) { + memcpy(xfer->buffer, data.ep3_buffer.out, len); + } else { + memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len); + } + xfer->buffer += len; + xfer->len -= len; + xfer->processed_len += len; + + if (xfer->len == 0 || len < xfer->max_size) { + xfer->valid = false; + dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true); + } + + if (ep == 0) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + } + } +} + +/* public functions */ +void dcd_init(uint8_t rhport) { + // init registers + USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN; + USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN; + USBOTG_FS->DEV_ADDR = 0x00; + + USBOTG_FS->INT_FG = 0xFF; + USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND; + + // setup endpoint 0 + EP_DMA(0) = (uint32_t) &data.buffer[0][0]; + EP_TX_LEN(0) = 0; + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + // enable other endpoints but NAK everything + USBOTG_FS->UEP4_1_MOD = 0xCC; + USBOTG_FS->UEP2_3_MOD = 0xCC; + USBOTG_FS->UEP5_6_MOD = 0xCC; + USBOTG_FS->UEP7_MOD = 0x0C; + + for (uint8_t ep = 1; ep < EP_MAX; ep++) { + EP_DMA(ep) = (uint32_t) &data.buffer[ep][0]; + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK; + } + EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0]; + + dcd_connect(rhport); +} + +void dcd_int_handler(uint8_t rhport) { + (void) rhport; + uint8_t status = USBOTG_FS->INT_FG; + if (status & USBFS_INT_FG_TRANSFER) { + uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST); + uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST); + + switch (token) { + case PID_OUT: { + uint16_t rx_len = USBOTG_FS->RX_LEN; + update_out(rhport, ep, rx_len); + break; + } + + case PID_IN: + update_in(rhport, ep, false); + break; + + case PID_SETUP: + // setup clears stall + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + data.ep0_tog = true; + dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true); + break; + } + + USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER; + } else if (status & USBFS_INT_FG_BUS_RST) { + data.ep0_tog = true; + data.xfer[0][TUSB_DIR_OUT].max_size = 64; + data.xfer[0][TUSB_DIR_IN].max_size = 64; + + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + + USBOTG_FS->DEV_ADDR = 0x00; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST; + } else if (status & USBFS_INT_FG_SUSPEND) { + dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND}; + dcd_event_handler(&event, true); + USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND; + } +} + +void dcd_int_enable(uint8_t rhport) { + (void) rhport; + NVIC_EnableIRQ(USBHD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) { + (void) rhport; + NVIC_DisableIRQ(USBHD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void) dev_addr; + dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response +} + +void dcd_remote_wakeup(uint8_t rhport) { + (void) rhport; + // TODO optional +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN; +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN; +} + +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + (void) en; + + // TODO implement later +} + +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { + (void) rhport; + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue; + } + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + (void) rhport; + uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress); + uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress); + TU_ASSERT(ep < EP_MAX); + + data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS; + data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep); + + if (ep != 0) { + if (dir == TUSB_DIR_OUT) { + if (data.isochronous[ep]) { + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET; + } else { + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK; + } + } else { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK; + } + } + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) { + (void) rhport; + // TODO optional +} + +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + (void) ep_addr; + // TODO optional +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + + struct usb_xfer* xfer = &data.xfer[ep][dir]; + dcd_int_disable(rhport); + xfer->valid = true; + xfer->buffer = buffer; + xfer->len = total_bytes; + xfer->processed_len = 0; + dcd_int_enable(rhport); + + if (dir == TUSB_DIR_IN) { + update_in(rhport, ep, true); + } + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + if (ep == 0) { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL; + } else { + EP_TX_LEN(0) = 0; + EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL; + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL; + } + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + if (ep == 0) { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK; + } + } +} + +#endif diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index 1f1c0b876..622f9c508 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -2,6 +2,7 @@ * The MIT License (MIT) * * Copyright (c) 2022 Greg Davill + * Copyright (c) 2023 Denis Krasutski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,366 +27,394 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && ((CFG_TUSB_MCU == OPT_MCU_CH32V307) || (CFG_TUSB_MCU == OPT_MCU_CH32F20X)) -#include "device/dcd.h" - +#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS #include "ch32_usbhs_reg.h" +#include "device/dcd.h" // Max number of bi-directional endpoints including EP0 -#define EP_MAX 16 +#define EP_MAX 16 typedef struct { - uint8_t *buffer; - // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API - uint16_t total_len; - uint16_t queued_len; - uint16_t max_size; - bool short_packet; + uint8_t* buffer; + uint16_t total_len; + uint16_t queued_len; + uint16_t max_size; + bool is_last_packet; + bool is_iso; } xfer_ctl_t; +typedef enum { + EP_RESPONSE_ACK, + EP_RESPONSE_NAK, +} ep_response_list_t; + #define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] static xfer_ctl_t xfer_status[EP_MAX][2]; -#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep)*2) -#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep)*4) -#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep)*4) -#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep)*2) +#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep) * 2) +#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep) * 4) +#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep) * 4) +#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep) * 2) #define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1)) #define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1)) /* Endpoint Buffer */ -TU_ATTR_ALIGNED(4) uint8_t EP0_DatabufHD[64]; // ep0(64) +TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE]; + +static void ep_set_response_and_toggle(uint8_t ep_num, tusb_dir_t ep_dir, ep_response_list_t response_type) { + if (ep_dir == TUSB_DIR_IN) { + uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_T_RES_ACK : USBHS_EP_T_RES_NAK; + if (ep_num == 0) { + if (response_type == EP_RESPONSE_ACK) { + if (EP_TX_LEN(ep_num) == 0) { + EP_TX_CTRL(ep_num) |= USBHS_EP_T_TOG_1; + } else { + EP_TX_CTRL(ep_num) ^= USBHS_EP_T_TOG_1; + } + } + } + if (xfer_status[ep_num][TUSB_DIR_IN].is_iso == true) { + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG; + } else { + EP_TX_CTRL(ep_num) = (EP_TX_CTRL(ep_num) & ~(USBHS_EP_T_RES_MASK)) | response; + } + } else { + uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_R_RES_ACK : USBHS_EP_R_RES_NAK; + if (ep_num == 0) { + if (response_type == EP_RESPONSE_ACK) { + if (xfer_status[ep_num][TUSB_DIR_OUT].queued_len == 0) { + EP_RX_CTRL(ep_num) |= USBHS_EP_R_TOG_1; + } + } else { + EP_RX_CTRL(ep_num) ^= USBHS_EP_R_TOG_1; + } + } + EP_RX_CTRL(ep_num) = (EP_RX_CTRL(ep_num) & ~(USBHS_EP_R_RES_MASK)) | response; + } +} + +static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer) { + if (ep_dir == TUSB_DIR_IN) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t next_tx_size = TU_MIN(remaining, xfer->max_size); + + if (ep_num == 0) { + memcpy(ep0_buffer, &xfer->buffer[xfer->queued_len], next_tx_size); + } else { + EP_TX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len]; + } + + EP_TX_LEN(ep_num) = next_tx_size; + xfer->queued_len += next_tx_size; + if (xfer->queued_len == xfer->total_len) { + xfer->is_last_packet = true; + } + if (xfer->is_iso == true) { + /* Enable EP to generate ISA_ACT interrupt */ + USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num); + } + } else { /* TUSB_DIR_OUT */ + uint16_t left_to_receive = xfer->total_len - xfer->queued_len; + uint16_t max_possible_rx_size = TU_MIN(xfer->max_size, left_to_receive); -volatile uint8_t USBHS_Dev_Endp0_Tog = 0x01; + if (max_possible_rx_size == left_to_receive) { + xfer->is_last_packet = true; + } + + if (ep_num > 0) { + EP_RX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len]; + EP_RX_MAX_LEN(ep_num) = max_possible_rx_size; + } + } + ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK); +} void dcd_init(uint8_t rhport) { - (void)rhport; + (void) rhport; - memset(&xfer_status, 0, sizeof(xfer_status)); + memset(&xfer_status, 0, sizeof(xfer_status)); - USBHSD->HOST_CTRL = 0x00; - USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM; + USBHSD->HOST_CTRL = 0x00; + USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM; - USBHSD->CONTROL = 0; + USBHSD->CONTROL = 0; #if TUD_OPT_HIGH_SPEED - USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; #else - #error OPT_MODE_FULL_SPEED not currently supported on CH32 - USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; + #error OPT_MODE_FULL_SPEED not currently supported on CH32 + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; #endif - USBHSD->INT_EN = 0; - USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_DETECT_EN | USBHS_SUSPEND_EN; - - /* ALL endpoint enable */ - USBHSD->ENDP_CONFIG = 0xffffffff; - - USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; - USBHSD->ENDP_TYPE = 0x00; - USBHSD->BUF_MODE = 0x00; + USBHSD->INT_EN = 0; + USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN | USBHS_ISO_ACT_EN; - USBHSD->UEP0_MAX_LEN = 64; + USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; + USBHSD->ENDP_TYPE = 0x00; + USBHSD->BUF_MODE = 0x00; - USBHSD->UEP0_DMA = (uint32_t)EP0_DatabufHD; + for (int ep = 0; ep < EP_MAX; ep++) { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; - USBHSD->UEP0_TX_LEN = 0; - USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_NAK; - USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK; + EP_RX_MAX_LEN(ep) = 0; + } - for (int ep = 1; ep < EP_MAX; ep++) { - EP_TX_LEN(ep) = 0; - EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; - EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + USBHSD->UEP0_DMA = (uint32_t) ep0_buffer; + USBHSD->UEP0_MAX_LEN = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE; - EP_RX_MAX_LEN(ep) = 512; - } - - USBHSD->DEV_AD = 0; - USBHSD->CONTROL |= USBHS_DEV_PU_EN; + USBHSD->DEV_AD = 0; + USBHSD->CONTROL |= USBHS_DEV_PU_EN; } void dcd_int_enable(uint8_t rhport) { - (void)rhport; - - NVIC_EnableIRQ(USBHS_IRQn); + (void) rhport; + NVIC_EnableIRQ(USBHS_IRQn); } void dcd_int_disable(uint8_t rhport) { - (void)rhport; - - NVIC_DisableIRQ(USBHS_IRQn); + (void) rhport; + NVIC_DisableIRQ(USBHS_IRQn); } void dcd_edpt_close_all(uint8_t rhport) { - (void)rhport; + (void) rhport; + + for (size_t ep = 1; ep < EP_MAX; ep++) { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + + EP_RX_MAX_LEN(ep) = 0; + } + + USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; } void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { - (void)dev_addr; + (void) dev_addr; - // Response with zlp status - dcd_edpt_xfer(rhport, 0x80, NULL, 0); + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); } -void dcd_remote_wakeup(uint8_t rhport) -{ +void dcd_remote_wakeup(uint8_t rhport) { (void) rhport; } -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { - (void)rhport; - - if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS) { - USBHSD->DEV_AD = (uint8_t)request->wValue; - } - - EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK; - EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK; +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + if (en) { + USBHSD->INT_EN |= USBHS_SOF_ACT_EN; + } else { + USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN); + } } -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) { - (void)rhport; - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < EP_MAX); - - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - - if (epnum != 0) { - if (tu_edpt_dir(desc_edpt->bEndpointAddress) == TUSB_DIR_OUT) { - EP_RX_CTRL(epnum) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_ACK; - } else { - EP_TX_LEN(epnum) = 0; - EP_TX_CTRL(epnum) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; - } - } - - return true; -} +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) { + (void) rhport; -int usbd_ep_close(const uint8_t ep) { - (void)ep; + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + USBHSD->DEV_AD = (uint8_t) request->wValue; + } - return 0; + EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + EP_RX_CTRL(0) = USBHS_EP_R_RES_NAK | USBHS_EP_R_TOG_0; } -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { - (void)rhport; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + (void) rhport; - if (epnum == 0) { - if (dir == TUSB_DIR_OUT) { - USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_STALL; - } else { - USBHSD->UEP0_TX_LEN = 0; - USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_STALL; - } - } else { - if (dir == TUSB_DIR_OUT) { - EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~USBHS_EP_R_RES_MASK) | USBHS_EP_R_RES_STALL; + uint8_t const ep_num = tu_edpt_number(desc_edpt->bEndpointAddress); + tusb_dir_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - } else { - EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~USBHS_EP_T_RES_MASK) | USBHS_EP_T_RES_STALL; - } - } -} + TU_ASSERT(ep_num < EP_MAX); -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { - (void)rhport; + if (ep_num == 0) { + return true; + } - 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(ep_num, dir); + xfer->max_size = tu_edpt_packet_size(desc_edpt); - if (epnum == 0) { - if (dir == TUSB_DIR_OUT) { - USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK; - } else { - } + xfer->is_iso = (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS); + if (dir == TUSB_DIR_OUT) { + USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << ep_num); + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + if (xfer->is_iso == true) { + USBHSD->ENDP_TYPE |= (USBHS_EP0_R_TYP << ep_num); + } + EP_RX_MAX_LEN(ep_num) = xfer->max_size; + } else { + if (xfer->is_iso == true) { + USBHSD->ENDP_TYPE |= (USBHS_EP0_T_TYP << ep_num); } else { - if (dir == TUSB_DIR_OUT) { - EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~(USBHS_EP_R_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_ACK; - - } else { - EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_NAK; - } + /* Enable all types except Isochronous to avoid ISO_ACT interrupt generation */ + USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num); } -} - -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { - (void)rhport; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + EP_TX_LEN(ep_num) = 0; + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + } - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API - xfer->total_len = total_bytes; - xfer->queued_len = 0; - xfer->short_packet = false; - - // uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t short_packet_size = total_bytes % (xfer->max_size + 1); + return true; +} - // Zero-size packet is special case. - if (short_packet_size == 0 || (total_bytes == 0)) { - xfer->short_packet = true; - } +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - if (!total_bytes) { - xfer->short_packet = true; - if (epnum == 0) { - USBHSD->UEP0_TX_LEN = 0; - USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0); - USBHS_Dev_Endp0_Tog ^= 1; - } else { - EP_TX_LEN(epnum) = 0; - EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK; - } - } else { - if (epnum == 0) { - xfer->queued_len += short_packet_size; - memcpy(&EP0_DatabufHD[0], buffer, short_packet_size); + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); - USBHSD->UEP0_TX_LEN = short_packet_size; - USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0); - USBHS_Dev_Endp0_Tog ^= 1; - } else { - xfer->queued_len += short_packet_size; + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + EP_RX_MAX_LEN(ep_num) = 0; + USBHSD->ENDP_TYPE &= ~(USBHS_EP0_R_TYP << ep_num); + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_R_EN << ep_num); + } else { // TUSB_DIR_IN + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + EP_TX_LEN(ep_num) = 0; + USBHSD->ENDP_TYPE &= ~(USBHS_EP0_T_TYP << ep_num); + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num); + } +} - EP_TX_DMA_ADDR(epnum) = (uint32_t)buffer; - USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << epnum); - EP_TX_LEN(epnum) = short_packet_size; - EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK; - } - } - } else { /* TUSB_DIR_OUT */ - if (epnum == 0) { - uint32_t read_count = USBHSD->RX_LEN; - read_count = TU_MIN(read_count, total_bytes); +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; - if ((total_bytes == 8)) { - read_count = 8; - memcpy(buffer, &EP0_DatabufHD[0], 8); - } else { - memcpy(buffer, &EP0_DatabufHD[0], read_count); - } - } else { - EP_RX_DMA_ADDR(epnum) = (uint32_t)xfer->buffer; - USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << epnum); - } + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); - // usbd_ep_read(ep_addr, buffer, total_bytes, &ret_bytes); - } - return true; + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL; + } else { + EP_TX_LEN(0) = 0; + EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL; + } } +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; -static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size) { - // xfer->queued_len = xfer->total_len - remaining; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); - uint16_t remaining = xfer->total_len - xfer->queued_len; - uint16_t to_recv_size; + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + } else { + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_R_RES_NAK; + } +} - if (remaining <= xfer->max_size) { - // Avoid buffer overflow. - to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; - } else { - // Room for full packet, choose recv_size based on what the microcontroller - // claims. - to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; - } +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + (void) rhport; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); - if (to_recv_size) { - } + xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir); + xfer->buffer = buffer; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + xfer->is_last_packet = false; - xfer->queued_len += xfer_size; + xfer_data_packet(ep_num, dir, xfer); - // Per USB spec, a short OUT packet (including length 0) is always - // indicative of the end of a transfer (at least for ctl, bulk, int). - xfer->short_packet = (xfer_size < xfer->max_size); + return true; } void dcd_int_handler(uint8_t rhport) { - (void)rhport; - - uint32_t end_num, rx_token; - uint8_t intflag = 0; - - intflag = USBHSD->INT_FG; - - if (intflag & USBHS_TRANSFER_FLAG) { - - end_num = (USBHSD->INT_ST) & MASK_UIS_ENDP; - rx_token = (((USBHSD->INT_ST) & MASK_UIS_TOKEN) >> 4) & 0x03; - - uint8_t endp = end_num | (rx_token == PID_IN ? TUSB_DIR_IN_MASK : 0); - - xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp)); - - if (rx_token == PID_OUT) { - uint16_t rx_len = USBHSD->RX_LEN; - - receive_packet(xfer, rx_len); + (void) rhport; - if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { - xfer->short_packet = false; + uint8_t int_flag = USBHSD->INT_FG; + uint8_t int_status = USBHSD->INT_ST; - dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true); - } + if (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)) { + uint8_t const token = int_status & MASK_UIS_TOKEN; - if (end_num == 0) { - USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; - } + if (token == USBHS_TOKEN_PID_SOF) { + uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK; + dcd_event_sof(rhport, frame_count, true); + }else { + uint8_t const ep_num = int_status & MASK_UIS_ENDP; + tusb_dir_t const ep_dir = (token == USBHS_TOKEN_PID_IN) ? TUSB_DIR_IN : TUSB_DIR_OUT; + uint8_t const ep_addr = tu_edpt_addr(ep_num, ep_dir); + xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, ep_dir); - } else if (rx_token == PID_IN) { - if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { - xfer->short_packet = false; - xfer->total_len = 0; - dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true); + if (token == USBHS_TOKEN_PID_OUT) { + uint16_t rx_len = USBHSD->RX_LEN; - EP_TX_CTRL(end_num) = (EP_TX_CTRL(end_num) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_NAK; + if (ep_num == 0) { + memcpy(&xfer->buffer[xfer->queued_len], ep0_buffer, rx_len); + } - if (end_num == 0) { - } - } else { - dcd_edpt_xfer(0, endp, xfer->buffer + xfer->queued_len, xfer->total_len - xfer->queued_len); - } + xfer->queued_len += rx_len; + if (rx_len < xfer->max_size) { + xfer->is_last_packet = true; + } + } else if (token == USBHS_TOKEN_PID_IN) { + if (xfer->is_iso && xfer->is_last_packet) { + /* Disable EP to avoid ISO_ACT interrupt generation */ + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num); + } else { + // Do nothing, no need to update xfer->is_last_packet, it is already updated in xfer_data_packet } + } - USBHSD->INT_FG = USBHS_TRANSFER_FLAG; /* Clear flag */ - } else if (intflag & USBHS_SETUP_FLAG) { - USBHS_Dev_Endp0_Tog = 1; - dcd_event_setup_received(0, EP0_DatabufHD, true); + if (xfer->is_last_packet == true) { + ep_set_response_and_toggle(ep_num, ep_dir, EP_RESPONSE_NAK); + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } else { + /* prepare next part of packet to xref */ + xfer_data_packet(ep_num, ep_dir, xfer); + } + } - USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */ - } else if (intflag & USBHS_DETECT_FLAG) { - USBHS_Dev_Endp0_Tog = 1; + USBHSD->INT_FG = (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)); /* Clear flag */ + } else if (int_flag & USBHS_SETUP_FLAG) { + ep_set_response_and_toggle(0, TUSB_DIR_IN, EP_RESPONSE_NAK); + ep_set_response_and_toggle(0, TUSB_DIR_OUT, EP_RESPONSE_NAK); + dcd_event_setup_received(0, ep0_buffer, true); - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN].max_size = 64; + USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */ + } else if (int_flag & USBHS_BUS_RST_FLAG) { + // TODO CH32 does not detect actual speed at this time (should be known at end of reset) + // This interrupt probably triggered at start of bus reset +// tusb_speed_t actual_speed; +// switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){ +// case USBHS_SPEED_TYPE_HIGH: +// actual_speed = TUSB_SPEED_HIGH; +// break; +// case USBHS_SPEED_TYPE_FULL: +// actual_speed = TUSB_SPEED_FULL; +// break; +// case USBHS_SPEED_TYPE_LOW: +// actual_speed = TUSB_SPEED_LOW; +// break; +// default: +// TU_ASSERT(0,); +// break; +// } +// dcd_event_bus_reset(0, actual_speed, true); - dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true); + dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true); - USBHSD->DEV_AD = 0; - USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; + USBHSD->DEV_AD = 0; + EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; + EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; - USBHSD->INT_FG = USBHS_DETECT_FLAG; /* Clear flag */ - } else if (intflag & USBHS_SUSPEND_FLAG) { - dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPEND }; - dcd_event_handler(&event, true); + USBHSD->INT_FG = USBHS_BUS_RST_FLAG; /* Clear flag */ + } else if (int_flag & USBHS_SUSPEND_FLAG) { + dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND}; + dcd_event_handler(&event, true); - USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */ - } + USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */ + } } #endif |
