diff options
| author | HiFiPhile <[email protected]> | 2024-05-13 21:08:11 +0200 |
|---|---|---|
| committer | HiFiPhile <[email protected]> | 2024-05-13 21:08:11 +0200 |
| commit | 0fce7d1f54f5fc405bee3ac20ad6bbdb966abcd0 (patch) | |
| tree | 1c3c32588f98b69f4fdca963d937ec5289ffc078 /src/portable | |
| parent | d0373f4749e320c7cb3fac9cce068b4398e60f2f (diff) | |
| parent | d707ea56b47e2fbbe5eaa4854a427a3a7873dea3 (diff) | |
Merge branch 'master' into test-mode-support
Diffstat (limited to 'src/portable')
23 files changed, 1749 insertions, 1850 deletions
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c index cc4799dd6..4dc93d2d8 100644 --- a/src/portable/analog/max3421/hcd_max3421.c +++ b/src/portable/analog/max3421/hcd_max3421.c @@ -30,6 +30,7 @@ #include <stdatomic.h> #include "host/hcd.h" +#include "host/usbh.h" //--------------------------------------------------------------------+ // @@ -166,6 +167,17 @@ enum { DEFAULT_HIEN = HIRQ_CONDET_IRQ | HIRQ_FRAME_IRQ | HIRQ_HXFRDN_IRQ | HIRQ_RCVDAV_IRQ }; +enum { + MAX_NAK_DEFAULT = 1 // Number of NAK per endpoint per usb frame +}; + +enum { + EP_STATE_IDLE = 0, + EP_STATE_COMPLETE = 1, + EP_STATE_ATTEMPT_1 = 2, // pending 1st attempt + EP_STATE_ATTEMPT_MAX = 15 +}; + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -173,18 +185,21 @@ enum { typedef struct { uint8_t daddr; - struct TU_ATTR_PACKED { - uint8_t ep_dir : 1; - uint8_t is_iso : 1; - uint8_t is_setup : 1; - uint8_t data_toggle : 1; - uint8_t xfer_pending : 1; - uint8_t xfer_complete : 1; + union { ; + struct TU_ATTR_PACKED { + uint8_t ep_num : 4; + uint8_t is_setup : 1; + uint8_t is_out : 1; + uint8_t is_iso : 1; + }hxfr_bm; + + uint8_t hxfr; }; struct TU_ATTR_PACKED { - uint8_t ep_num : 4; - uint16_t packet_size : 12; + uint8_t state : 4; + uint8_t data_toggle : 1; + uint16_t packet_size : 11; }; uint16_t total_len; @@ -195,6 +210,8 @@ typedef struct { TU_VERIFY_STATIC(sizeof(max3421_ep_t) == 12, "size is not correct"); typedef struct { + volatile uint16_t frame_count; + // cached register uint8_t sndbc; uint8_t hirq; @@ -204,18 +221,24 @@ typedef struct { uint8_t hxfr; atomic_flag busy; // busy transferring - volatile uint16_t frame_count; - - max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0 - OSAL_MUTEX_DEF(spi_mutexdef); #if OSAL_MUTEX_REQUIRED + OSAL_MUTEX_DEF(spi_mutexdef); osal_mutex_t spi_mutex; #endif + + max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0 } max3421_data_t; static max3421_data_t _hcd_data; +// max NAK before giving up in a frame. 0 means infinite NAKs +static tuh_configure_max3421_t _tuh_cfg = { + .max_nak = MAX_NAK_DEFAULT, + .cpuctl = 0, // default: INT pulse width = 10.6 us + .pinctl = 0, // default: negative edge interrupt +}; + //--------------------------------------------------------------------+ // API: SPI transfer with MAX3421E // - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application @@ -304,7 +327,6 @@ static void fifo_write(uint8_t rhport, uint8_t reg, uint8_t const * buffer, uint tuh_max3421_spi_xfer_api(rhport, buffer, NULL, len); max3421_spi_unlock(rhport, in_isr); - } static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) { @@ -321,35 +343,35 @@ static void fifo_read(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_is } //------------- register write helper -------------// -static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) { reg_write(rhport, HIRQ_ADDR, data, in_isr); // HIRQ write 1 is clear _hcd_data.hirq &= (uint8_t) ~data; } -static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) { _hcd_data.hien = data; reg_write(rhport, HIEN_ADDR, data, in_isr); } -static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) { _hcd_data.mode = data; reg_write(rhport, MODE_ADDR, data, in_isr); } -static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) { if ( _hcd_data.peraddr == data ) return; // no need to change address _hcd_data.peraddr = data; reg_write(rhport, PERADDR_ADDR, data, in_isr); } -static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) { _hcd_data.hxfr = data; reg_write(rhport, HXFR_ADDR, data, in_isr); } -static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) { +TU_ATTR_ALWAYS_INLINE static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) { _hcd_data.sndbc = data; reg_write(rhport, SNDBC_ADDR, data, in_isr); } @@ -359,10 +381,11 @@ static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) { //--------------------------------------------------------------------+ static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) { + uint8_t const is_out = 1-ep_dir; for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) { max3421_ep_t* ep = &_hcd_data.ep[i]; - // for control endpoint, skip direction check - if (daddr == ep->daddr && ep_num == ep->ep_num && (ep_dir == ep->ep_dir || ep_num == 0)) { + // control endpoint is bi-direction (skip check) + if (daddr == ep->daddr && ep_num == ep->hxfr_bm.ep_num && (ep_num == 0 || is_out == ep->hxfr_bm.is_out)) { return ep; } } @@ -393,14 +416,23 @@ static void free_ep(uint8_t daddr) { } } +// Check if endpoint has an queued transfer and not reach max NAK +TU_ATTR_ALWAYS_INLINE static inline bool is_ep_pending(max3421_ep_t const * ep) { + uint8_t const state = ep->state; + return ep->packet_size && (state >= EP_STATE_ATTEMPT_1) && + (_tuh_cfg.max_nak == 0 || state < EP_STATE_ATTEMPT_1 + _tuh_cfg.max_nak); +} + +// Find the next pending endpoint using round-robin scheduling, starting from next endpoint. +// return NULL if not found +// TODO respect interrupt endpoint's interval static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) { size_t const idx = (size_t) (cur_ep - _hcd_data.ep); // starting from next endpoint for (size_t i = idx + 1; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) { max3421_ep_t* ep = &_hcd_data.ep[i]; - if (ep->xfer_pending && ep->packet_size) { -// TU_LOG3("next pending i = %u\r\n", i); + if (is_ep_pending(ep)) { return ep; } } @@ -408,8 +440,7 @@ static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) { // wrap around including current endpoint for (size_t i = 0; i <= idx; i++) { max3421_ep_t* ep = &_hcd_data.ep[i]; - if (ep->xfer_pending && ep->packet_size) { -// TU_LOG3("next pending i = %u\r\n", i); + if (is_ep_pending(ep)) { return ep; } } @@ -424,10 +455,11 @@ static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) { // optional hcd configuration, called by tuh_configure() bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { (void) rhport; - (void) cfg_id; - (void) cfg_param; + TU_VERIFY(cfg_id == TUH_CFGID_MAX3421 && cfg_param != NULL); - return false; + tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param; + _tuh_cfg = cfg->max3421; + return true; } // Initialize controller to host mode @@ -438,6 +470,7 @@ bool hcd_init(uint8_t rhport) { TU_LOG2_INT(sizeof(max3421_ep_t)); TU_LOG2_INT(sizeof(max3421_data_t)); + TU_LOG2_INT(offsetof(max3421_data_t, ep)); tu_memclr(&_hcd_data, sizeof(_hcd_data)); _hcd_data.peraddr = 0xff; // invalid @@ -446,13 +479,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, PINCTL_FDUPSPI, false); + reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false); - // V1 is 0x01, V2 is 0x12, V3 is 0x13 + // 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); @@ -476,7 +511,25 @@ bool hcd_init(uint8_t rhport) { tuh_max3421_int_api(rhport, true); // Enable Interrupt pin - reg_write(rhport, CPUCTL_ADDR, CPUCTL_IE, false); + reg_write(rhport, CPUCTL_ADDR, _tuh_cfg.cpuctl | CPUCTL_IE, false); + + return true; +} + +bool hcd_deinit(uint8_t rhport) { + (void) rhport; + + // disable interrupt + tuh_max3421_int_api(rhport, false); + + // reset max3421 and power down + reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false); + reg_write(rhport, USBCTL_ADDR, USBCTL_PWRDOWN, false); + + #if OSAL_MUTEX_REQUIRED + osal_mutex_delete(_hcd_data.spi_mutex); + _hcd_data.spi_mutex = NULL; + #endif return true; } @@ -539,7 +592,6 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // Open an endpoint bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc) { (void) rhport; - (void) daddr; uint8_t const ep_num = tu_edpt_number(ep_desc->bEndpointAddress); tusb_dir_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress); @@ -551,12 +603,9 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e ep = allocate_ep(); TU_ASSERT(ep); ep->daddr = daddr; - ep->ep_num = (uint8_t) (ep_num & 0x0f); - ep->ep_dir = (ep_dir == TUSB_DIR_IN) ? 1 : 0; - } - - if ( TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer ) { - ep->is_iso = 1; + ep->hxfr_bm.ep_num = (uint8_t) (ep_num & 0x0f); + ep->hxfr_bm.is_out = (ep_dir == TUSB_DIR_OUT) ? 1 : 0; + ep->hxfr_bm.is_iso = (TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer) ? 1 : 0; } ep->packet_size = (uint16_t) (tu_edpt_packet_size(ep_desc) & 0x7ff); @@ -564,7 +613,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e return true; } -void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { +static void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { // Page 12: Programming BULK-OUT Transfers // TODO double buffered if (switch_ep) { @@ -580,12 +629,10 @@ void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { fifo_write(rhport, SNDFIFO_ADDR, ep->buf, xact_len, in_isr); } sndbc_write(rhport, xact_len, in_isr); - - uint8_t const hxfr = (uint8_t ) (ep->ep_num | HXFR_OUT_NIN | (ep->is_iso ? HXFR_ISO : 0)); - hxfr_write(rhport, hxfr, in_isr); + hxfr_write(rhport, ep->hxfr, in_isr); } -void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { +static void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { // Page 13: Programming BULK-IN Transfers if (switch_ep) { peraddr_write(rhport, ep->daddr, in_isr); @@ -594,33 +641,36 @@ void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { reg_write(rhport, HCTL_ADDR, hctl, in_isr); } - uint8_t const hxfr = (uint8_t) (ep->ep_num | (ep->is_iso ? HXFR_ISO : 0)); - hxfr_write(rhport, hxfr, in_isr); + hxfr_write(rhport, ep->hxfr, in_isr); +} + +static void xact_setup(uint8_t rhport, max3421_ep_t *ep, bool in_isr) { + peraddr_write(rhport, ep->daddr, in_isr); + fifo_write(rhport, SUDFIFO_ADDR, ep->buf, 8, in_isr); + hxfr_write(rhport, HXFR_SETUP, in_isr); } -TU_ATTR_ALWAYS_INLINE static inline void xact_inout(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { - if (ep->ep_num == 0 ) { +static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) { + if (ep->hxfr_bm.ep_num == 0 ) { // setup - if (ep->is_setup) { - peraddr_write(rhport, ep->daddr, in_isr); - fifo_write(rhport, SUDFIFO_ADDR, ep->buf, 8, in_isr); - hxfr_write(rhport, HXFR_SETUP, in_isr); + if (ep->hxfr_bm.is_setup) { + xact_setup(rhport, ep, in_isr); return; } // status if (ep->buf == NULL || ep->total_len == 0) { - uint8_t const hxfr = HXFR_HS | (ep->ep_dir ? 0 : HXFR_OUT_NIN); + uint8_t const hxfr = (uint8_t) (HXFR_HS | (ep->hxfr & HXFR_OUT_NIN)); peraddr_write(rhport, ep->daddr, in_isr); hxfr_write(rhport, hxfr, in_isr); return; } } - if (ep->ep_dir) { - xact_in(rhport, ep, switch_ep, in_isr); - }else { + if (ep->hxfr_bm.is_out) { xact_out(rhport, ep, switch_ep, in_isr); + }else { + xact_in(rhport, ep, switch_ep, in_isr); } } @@ -632,25 +682,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir); TU_VERIFY(ep); - // control transfer can switch direction - ep->ep_dir = ep_dir ? 1u : 0u; + if (ep_num == 0) { + // control transfer can switch direction + ep->hxfr_bm.is_out = ep_dir ? 0 : 1; + ep->hxfr_bm.is_setup = 0; + ep->data_toggle = 1; + } ep->buf = buffer; ep->total_len = buflen; ep->xferred_len = 0; - ep->xfer_complete = 0; - ep->xfer_pending = 1; - - if ( ep_num == 0 ) { - ep->is_setup = 0; - ep->data_toggle = 1; - } + ep->state = EP_STATE_ATTEMPT_1; // carry out transfer if not busy - if ( !atomic_flag_test_and_set(&_hcd_data.busy) ) { - xact_inout(rhport, ep, true, false); - } else { - return true; + if (!atomic_flag_test_and_set(&_hcd_data.busy)) { + xact_generic(rhport, ep, true, false); } return true; @@ -673,17 +719,16 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8] max3421_ep_t* ep = find_opened_ep(daddr, 0, 0); TU_ASSERT(ep); - ep->ep_dir = 0; - ep->is_setup = 1; + ep->hxfr_bm.is_out = 1; + ep->hxfr_bm.is_setup = 1; ep->buf = (uint8_t*)(uintptr_t) setup_packet; ep->total_len = 8; ep->xferred_len = 0; - ep->xfer_complete = 0; - ep->xfer_pending = 1; + ep->state = EP_STATE_ATTEMPT_1; // carry out transfer if not busy - if ( !atomic_flag_test_and_set(&_hcd_data.busy) ) { - xact_inout(rhport, ep, true, false); + if (!atomic_flag_test_and_set(&_hcd_data.busy)) { + xact_setup(rhport, ep, false); } return true; @@ -748,22 +793,23 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) { } static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) { - uint8_t const ep_addr = tu_edpt_addr(ep->ep_num, ep->ep_dir); + uint8_t const ep_dir = 1-ep->hxfr_bm.is_out; + uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir); // save data toggle - if (ep->ep_dir) { + if (ep_dir) { ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1u : 0u; }else { ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1u : 0u; } - ep->xfer_pending = 0; + ep->state = EP_STATE_IDLE; hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, in_isr); // Find next pending endpoint - max3421_ep_t *next_ep = find_next_pending_ep(ep); + max3421_ep_t * next_ep = find_next_pending_ep(ep); if (next_ep) { - xact_inout(rhport, next_ep, true, in_isr); + xact_generic(rhport, next_ep, true, in_isr); }else { // no more pending atomic_flag_clear(&_hcd_data.busy); @@ -793,20 +839,23 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) { case HRSL_NAK: if (ep_num == 0) { - // NAK on control, retry immediately + // control endpoint -> retry immediately hxfr_write(rhport, _hcd_data.hxfr, in_isr); - }else { - // NAK on non-control, find next pending to switch - max3421_ep_t *next_ep = find_next_pending_ep(ep); + } else { + if (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 + // this endpoint is only one pending -> retry immediately hxfr_write(rhport, _hcd_data.hxfr, in_isr); - }else if (next_ep) { - // switch to next pending TODO could have issue with double buffered if not clear previously out data - xact_inout(rhport, next_ep, true, in_isr); - }else { - TU_ASSERT(false,); + } 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; @@ -828,12 +877,14 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) { if (ep_dir) { // IN transfer: fifo data is already received in RCVDAV IRQ - if ( hxfr_type & HXFR_HS ) { - ep->xfer_complete = 1; + + // mark control handshake as complete + if (hxfr_type & HXFR_HS) { + ep->state = EP_STATE_COMPLETE; } // short packet or all bytes transferred - if ( ep->xfer_complete ) { + if (ep->state == EP_STATE_COMPLETE) { xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr); }else { // more to transfer @@ -867,13 +918,13 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) { void print_hirq(uint8_t hirq) { TU_LOG3_HEX(hirq); - if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN"); - if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME"); - if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET"); - if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN"); - if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV"); - if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV"); - if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU"); + if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN"); + if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME"); + if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET"); + if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN"); + if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV"); + if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV"); + if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU"); if (hirq & HIRQ_BUSEVENT_IRQ) TU_LOG3(" BUSEVENT"); TU_LOG3("\r\n"); @@ -890,6 +941,25 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (hirq & HIRQ_FRAME_IRQ) { _hcd_data.frame_count++; + + 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) { + ep->state = EP_STATE_ATTEMPT_1; + + if (ep_retry == NULL) { + ep_retry = ep; + } + } + } + + // start usb transfer if not busy + if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) { + xact_generic(rhport, ep_retry, true, in_isr); + } } if (hirq & HIRQ_CONDET_IRQ) { @@ -898,17 +968,17 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { // queue more transfer in handle_xfer_done() can cause hirq to be set again while external IRQ may not catch and/or // not call this handler again. So we need to loop until all IRQ are cleared - while ( hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ) ) { - if ( hirq & HIRQ_RCVDAV_IRQ ) { + while (hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ)) { + if (hirq & HIRQ_RCVDAV_IRQ) { uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK; - max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1); + max3421_ep_t* ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1); uint8_t xact_len = 0; // RCVDAV_IRQ can trigger 2 times (dual buffered) - while ( hirq & HIRQ_RCVDAV_IRQ ) { + while (hirq & HIRQ_RCVDAV_IRQ) { uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, in_isr); xact_len = (uint8_t) tu_min16(rcvbc, ep->total_len - ep->xferred_len); - if ( xact_len ) { + if (xact_len) { fifo_read(rhport, ep->buf, xact_len, in_isr); ep->buf += xact_len; ep->xferred_len += xact_len; @@ -919,12 +989,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { hirq = reg_read(rhport, HIRQ_ADDR, in_isr); } - if ( xact_len < ep->packet_size || ep->xferred_len >= ep->total_len ) { - ep->xfer_complete = 1; + if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) { + ep->state = EP_STATE_COMPLETE; } } - if ( hirq & HIRQ_HXFRDN_IRQ ) { + if (hirq & HIRQ_HXFRDN_IRQ) { hirq_write(rhport, HIRQ_HXFRDN_IRQ, in_isr); handle_xfer_done(rhport, in_isr); } diff --git a/src/portable/chipidea/ci_fs/ci_fs_kinetis.h b/src/portable/chipidea/ci_fs/ci_fs_kinetis.h index cd21af1c7..31e14a546 100644 --- a/src/portable/chipidea/ci_fs/ci_fs_kinetis.h +++ b/src/portable/chipidea/ci_fs/ci_fs_kinetis.h @@ -36,14 +36,12 @@ #define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE) #define CI_REG CI_FS_REG(0) -void dcd_int_enable(uint8_t rhport) -{ +void dcd_int_enable(uint8_t rhport) { (void) rhport; NVIC_EnableIRQ(USB0_IRQn); } -void dcd_int_disable(uint8_t rhport) -{ +void dcd_int_disable(uint8_t rhport) { (void) rhport; NVIC_DisableIRQ(USB0_IRQn); } diff --git a/src/portable/chipidea/ci_fs/ci_fs_mcx.h b/src/portable/chipidea/ci_fs/ci_fs_mcx.h index 3bfcd398e..4b93a03a7 100644 --- a/src/portable/chipidea/ci_fs/ci_fs_mcx.h +++ b/src/portable/chipidea/ci_fs/ci_fs_mcx.h @@ -29,19 +29,28 @@ #include "fsl_device_registers.h" -#define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE) -#define CI_REG CI_FS_REG(0) +#if CFG_TUSB_MCU == OPT_MCU_MCXN9 + #define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE) + #define CIFS_IRQN USB0_FS_IRQn -void dcd_int_enable(uint8_t rhport) -{ +#elif CFG_TUSB_MCU == OPT_MCU_MCXA15 + #define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE) + #define CIFS_IRQN USB0_IRQn + +#else + #error "MCU is not supported" +#endif + +#define CI_REG CI_FS_REG(0) + +void dcd_int_enable(uint8_t rhport) { (void) rhport; - NVIC_EnableIRQ(USB0_FS_IRQn); + NVIC_EnableIRQ(CIFS_IRQN); } -void dcd_int_disable(uint8_t rhport) -{ +void dcd_int_disable(uint8_t rhport) { (void) rhport; - NVIC_DisableIRQ(USB0_FS_IRQn); + NVIC_DisableIRQ(CIFS_IRQN); } #endif diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c index 9327e09d8..02f813ab5 100644 --- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c +++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c @@ -271,9 +271,21 @@ void dcd_init(uint8_t rhport) { (void) rhport; + // save crystal-less setting (if available) + #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1 + uint32_t clk_recover_irc_en = CI_REG->CLK_RECOVER_IRC_EN; + uint32_t clk_recover_ctrl = CI_REG->CLK_RECOVER_CTRL; + #endif + CI_REG->USBTRC0 |= USB_USBTRC0_USBRESET_MASK; while (CI_REG->USBTRC0 & USB_USBTRC0_USBRESET_MASK); + // restore crystal-less setting (if available) + #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1 + CI_REG->CLK_RECOVER_IRC_EN = clk_recover_irc_en; + CI_REG->CLK_RECOVER_CTRL |= clk_recover_ctrl; + #endif + tu_memclr(&_dcd, sizeof(_dcd)); CI_REG->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */ CI_REG->BDT_PAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8); @@ -283,7 +295,7 @@ void dcd_init(uint8_t rhport) CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK; dcd_connect(rhport); - // NVIC_ClearPendingIRQ(USB0_IRQn); + // NVIC_ClearPendingIRQ(CIFS_IRQN); } void dcd_set_address(uint8_t rhport, uint8_t dev_addr) 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 1fcfb6241..bfc0baa56 100644 --- a/src/portable/espressif/esp32sx/dcd_esp32sx.c +++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c @@ -31,16 +31,26 @@ #if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED) // Espressif -#include "freertos/xtensa_api.h" +#include "xtensa_api.h" #include "esp_intr_alloc.h" #include "esp_log.h" #include "soc/dport_reg.h" #include "soc/gpio_sig_map.h" #include "soc/usb_periph.h" +#include "soc/usb_reg.h" +#include "soc/usb_struct.h" #include "soc/periph_defs.h" // for interrupt source #include "device/dcd.h" +#ifndef USB_OUT_EP_NUM +#define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0]))) +#endif + +#ifndef USB_IN_EP_NUM +#define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0]))) +#endif + // Max number of bi-directional endpoints including EP0 // Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0 // We should probably prohibit enabling Endpoint IN > 4 (not done yet) 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 4e702aed4..2fe721d6b 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,82 @@ 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; +} _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, true); + } 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 +198,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 +212,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 +227,22 @@ static void xact_in_dma(uint8_t epnum) //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ -void dcd_init (uint8_t rhport) -{ - TU_LOG1("dcd init\r\n"); +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 +257,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 +266,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,14 +276,12 @@ 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; @@ -316,39 +291,32 @@ void dcd_sof_enable(uint8_t rhport, bool en) //--------------------------------------------------------------------+ // 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 +329,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 +340,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 +380,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 +470,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 +513,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 +541,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,32 +583,31 @@ 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; - xact_out_dma(EP_ISO_NUM); + // Transfer from endpoint to RAM only if data is not corrupted + if ((int_status & USBD_INTEN_USBEVENT_Msk) == 0 || + (NRF_USBD->EVENTCAUSE & USBD_EVENTCAUSE_ISOOUTCRC_Msk) == 0) { + xact_out_dma(EP_ISO_NUM); + } } // 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 ) - { + 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; @@ -679,16 +616,17 @@ void dcd_int_handler(uint8_t rhport) dcd_event_bus_signal(0, DCD_EVENT_SOF, true); } - if ( int_status & USBD_INTEN_USBEVENT_Msk ) - { - TU_LOG(2, "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 }; + 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; @@ -696,8 +634,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; @@ -709,34 +646,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(); @@ -778,30 +710,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; @@ -815,11 +741,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. @@ -828,22 +754,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); } @@ -851,17 +773,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; @@ -885,76 +803,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 @@ -967,8 +889,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, @@ -977,50 +898,41 @@ void tusb_hal_nrf_power_event (uint32_t event) }; #if CFG_TUSB_DEBUG >= 2 - const char* const power_evt_str[] = { "Detected", "Removed", "Ready" }; + const char* const power_evt_str[] = {"Detected", "Removed", "Ready"}; TU_LOG(2, "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(); } @@ -1028,64 +940,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 @@ -1097,30 +1003,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); @@ -1129,15 +1036,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 5c65ea33d..dc71117b3 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -269,9 +269,21 @@ void dcd_init(uint8_t rhport) { (void) rhport; + // save crystal-less setting (if available) + #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1 + uint32_t clk_recover_irc_en = KHCI->CLK_RECOVER_IRC_EN; + uint32_t clk_recover_ctrl = KHCI->CLK_RECOVER_CTRL; + #endif + KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK; while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK); + // restore crystal-less setting (if available) + #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1 + KHCI->CLK_RECOVER_IRC_EN = clk_recover_irc_en; + KHCI->CLK_RECOVER_CTRL |= clk_recover_ctrl; + #endif + tu_memclr(&_dcd, sizeof(_dcd)); KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */ KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8); 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/ohci/ohci.h b/src/portable/ohci/ohci.h index 4feefd771..94bad5df7 100644 --- a/src/portable/ohci/ohci.h +++ b/src/portable/ohci/ohci.h @@ -83,7 +83,7 @@ typedef struct TU_ATTR_ALIGNED(16) volatile uint32_t condition_code : 4; // Word 1 - volatile uint8_t* current_buffer_pointer; + uint8_t* volatile current_buffer_pointer; // Word 2 : next TD volatile uint32_t next; diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index e8cee73fd..bc0deee32 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -48,7 +48,7 @@ *------------------------------------------------------------------*/ // Init these in dcd_init -static uint8_t *next_buffer_ptr; +static uint8_t* next_buffer_ptr; // USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in. static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; @@ -56,79 +56,70 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; // SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd static bool _sof_enable = false; -TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) -{ +TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) { return &hw_endpoints[num][dir]; } -static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr) -{ +TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) { uint8_t num = tu_edpt_number(ep_addr); tusb_dir_t dir = tu_edpt_dir(ep_addr); return hw_endpoint_get_by_num(num, dir); } -static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type) -{ +static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) { // size must be multiple of 64 uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u; // double buffered Bulk endpoint - if ( transfer_type == TUSB_XFER_BULK ) - { + if (transfer_type == TUSB_XFER_BULK) { size *= 2u; } ep->hw_data_buf = next_buffer_ptr; next_buffer_ptr += size; - assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0); + assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0); uint dpram_offset = hw_data_offset(ep->hw_data_buf); hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf); // Fill in endpoint control register with buffer offset - uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; *ep->endpoint_control = reg; } -static void _hw_endpoint_close(struct hw_endpoint *ep) -{ - // Clear hardware registers and then zero the struct - // Clears endpoint enable - *ep->endpoint_control = 0; - // Clears buffer available, etc - *ep->buffer_control = 0; - // Clear any endpoint state - memset(ep, 0, sizeof(struct hw_endpoint)); +static void _hw_endpoint_close(struct hw_endpoint* ep) { + // Clear hardware registers and then zero the struct + // Clears endpoint enable + *ep->endpoint_control = 0; + // Clears buffer available, etc + *ep->buffer_control = 0; + // Clear any endpoint state + memset(ep, 0, sizeof(struct hw_endpoint)); - // Reclaim buffer space if all endpoints are closed - bool reclaim_buffers = true; - for ( uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++ ) - { - if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) - { - reclaim_buffers = false; - break; - } - } - if (reclaim_buffers) - { - next_buffer_ptr = &usb_dpram->epx_data[0]; + // Reclaim buffer space if all endpoints are closed + bool reclaim_buffers = true; + for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) { + if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || + hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) { + reclaim_buffers = false; + break; } + } + if (reclaim_buffers) { + next_buffer_ptr = &usb_dpram->epx_data[0]; + } } -static void hw_endpoint_close(uint8_t ep_addr) -{ - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); - _hw_endpoint_close(ep); +static void hw_endpoint_close(uint8_t ep_addr) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + _hw_endpoint_close(ep); } -static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) -{ - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); +static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); const uint8_t num = tu_edpt_number(ep_addr); const tusb_dir_t dir = tu_edpt_dir(ep_addr); @@ -143,35 +134,26 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t ep->transfer_type = transfer_type; // Every endpoint has a buffer control register in dpram - if ( dir == TUSB_DIR_IN ) - { + if (dir == TUSB_DIR_IN) { ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in; - } - else - { + } else { ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out; } // Clear existing buffer control state *ep->buffer_control = 0; - if ( num == 0 ) - { + if (num == 0) { // EP0 has no endpoint control register because the buffer offsets are fixed ep->endpoint_control = NULL; // Buffer offset is fixed (also double buffered) ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0]; - } - else - { + } else { // Set the endpoint control register (starts at EP1, hence num-1) - if ( dir == TUSB_DIR_IN ) - { + if (dir == TUSB_DIR_IN) { ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in; - } - else - { + } else { ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; } @@ -180,76 +162,82 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t } } -static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); - hw_endpoint_xfer_start(ep, buffer, total_bytes); +static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + hw_endpoint_xfer_start(ep, buffer, total_bytes); } -static void __tusb_irq_path_func(hw_handle_buff_status)(void) -{ - uint32_t remaining_buffers = usb_hw->buf_status; - pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); - uint bit = 1u; - for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) - { - if (remaining_buffers & bit) - { - // clear this in advance - usb_hw_clear->buf_status = bit; +static void __tusb_irq_path_func(hw_handle_buff_status)(void) { + uint32_t remaining_buffers = usb_hw->buf_status; + pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers); + uint bit = 1u; + for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) { + if (remaining_buffers & bit) { + // clear this in advance + usb_hw_clear->buf_status = bit; - // IN transfer for even i, OUT transfer for odd i - struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); + // IN transfer for even i, OUT transfer for odd i + struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN); - // Continue xfer - bool done = hw_endpoint_xfer_continue(ep); - if (done) - { - // Notify - dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true); - hw_endpoint_reset_transfer(ep); - } - remaining_buffers &= ~bit; - } - bit <<= 1u; + // Continue xfer + bool done = hw_endpoint_xfer_continue(ep); + if (done) { + // Notify + dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true); + hw_endpoint_reset_transfer(ep); + } + remaining_buffers &= ~bit; } + bit <<= 1u; + } } -TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void) -{ - // If we have finished this transfer on EP0 set pid back to 1 for next - // setup transfer. Also clear a stall in case - uint8_t addrs[] = {0x0, 0x80}; - for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++) - { - struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]); - ep->next_pid = 1u; +TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) { + // If we have finished this transfer on EP0 set pid back to 1 for next + // setup transfer. Also clear a stall in case + for (uint8_t dir = 0; dir < 2; dir++) { + struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir); + if (ep->active) { + // Abort any pending transfer from a prior control transfer per USB specs + // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1). + // Which means we are not guaranteed to safely abort pending transfer on B0 and B1. + uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS); + if (rp2040_chip_version() >= 2) { + usb_hw_set->abort = abort_mask; + while ((usb_hw->abort_done & abort_mask) != abort_mask) {} + } + + _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL); + hw_endpoint_reset_transfer(ep); + + if (rp2040_chip_version() >= 2) { + usb_hw_clear->abort_done = abort_mask; + usb_hw_clear->abort = abort_mask; + } } + ep->next_pid = 1u; + } } -static void __tusb_irq_path_func(reset_non_control_endpoints)(void) -{ +static void __tusb_irq_path_func(reset_non_control_endpoints)(void) { // Disable all non-control - for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ ) - { + for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) { usb_dpram->ep_ctrl[i].in = 0; usb_dpram->ep_ctrl[i].out = 0; } // clear non-control hw endpoints - tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t)); + tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t)); // reclaim buffer space next_buffer_ptr = &usb_dpram->epx_data[0]; } -static void __tusb_irq_path_func(dcd_rp2040_irq)(void) -{ +static void __tusb_irq_path_func(dcd_rp2040_irq)(void) { uint32_t const status = usb_hw->ints; uint32_t handled = 0; - if ( status & USB_INTF_DEV_SOF_BITS ) - { + if (status & USB_INTF_DEV_SOF_BITS) { bool keep_sof_alive = false; handled |= USB_INTF_DEV_SOF_BITS; @@ -258,20 +246,17 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) // Errata 15 workaround for Device Bulk-In endpoint e15_last_sof = time_us_32(); - for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++ ) - { - struct hw_endpoint * ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN); + for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) { + struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN); // Active Bulk IN endpoint requires SOF - if ( (ep->transfer_type == TUSB_XFER_BULK) && ep->active ) - { + if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) { keep_sof_alive = true; hw_endpoint_lock_update(ep, 1); // Deferred enable? - if ( ep->pending ) - { + if (ep->pending) { ep->pending = 0; hw_endpoint_start_next_buffer(ep); } @@ -282,26 +267,24 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) #endif // disable SOF interrupt if it is used for RESUME in remote wakeup - if ( !keep_sof_alive && !_sof_enable ) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; + if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true); } // xfer events are handled before setup req. So if a transfer completes immediately // before closing the EP, the events will be delivered in same order. - if ( status & USB_INTS_BUFF_STATUS_BITS ) - { + if (status & USB_INTS_BUFF_STATUS_BITS) { handled |= USB_INTS_BUFF_STATUS_BITS; hw_handle_buff_status(); } - if ( status & USB_INTS_SETUP_REQ_BITS ) - { + if (status & USB_INTS_SETUP_REQ_BITS) { handled |= USB_INTS_SETUP_REQ_BITS; - uint8_t const * setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet); + uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet); // reset pid to both 1 (data and ack) - reset_ep0_pid(); + reset_ep0(); // Pass setup packet to tiny usb dcd_event_setup_received(0, setup, true); @@ -329,8 +312,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) #endif // SE0 for 2.5 us or more (will last at least 10ms) - if ( status & USB_INTS_BUS_RESET_BITS ) - { + if (status & USB_INTS_BUS_RESET_BITS) { pico_trace("BUS RESET\r\n"); handled |= USB_INTS_BUS_RESET_BITS; @@ -342,7 +324,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) #if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX // Only run enumeration workaround if pull up is enabled - if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix(); + if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix(); #endif } @@ -354,22 +336,19 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) * because without VBUS detection, it is impossible to tell the difference between * being disconnected and suspended. */ - if ( status & USB_INTS_DEV_SUSPEND_BITS ) - { + if (status & USB_INTS_DEV_SUSPEND_BITS) { handled |= USB_INTS_DEV_SUSPEND_BITS; dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS; } - if ( status & USB_INTS_DEV_RESUME_FROM_HOST_BITS ) - { + if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) { handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS; dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS; } - if ( status ^ handled ) - { + if (status ^ handled) { panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); } } @@ -390,10 +369,11 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) #define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff #endif -void dcd_init (uint8_t rhport) -{ +void dcd_init(uint8_t rhport) { assert(rhport == 0); + TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version()); + // Reset hardware to default state rp2040_usb_init(); @@ -405,7 +385,7 @@ void dcd_init (uint8_t rhport) irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY); // Init control endpoints - tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t)); + tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t)); hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL); hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL); @@ -420,27 +400,37 @@ void dcd_init (uint8_t rhport) // for the global interrupt enable... // Note: Force VBUS detect cause disconnection not detectable usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; - usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS | - USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS | - (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS); + usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS | + USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS | + (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS); dcd_connect(rhport); } -void dcd_int_enable(__unused uint8_t rhport) -{ - assert(rhport == 0); - irq_set_enabled(USBCTRL_IRQ, true); +bool dcd_deinit(uint8_t rhport) { + (void) rhport; + + reset_non_control_endpoints(); + irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq); + + // reset usb hardware into initial state + reset_block(RESETS_RESET_USBCTRL_BITS); + unreset_block_wait(RESETS_RESET_USBCTRL_BITS); + + return true; +} + +void dcd_int_enable(__unused uint8_t rhport) { + assert(rhport == 0); + irq_set_enabled(USBCTRL_IRQ, true); } -void dcd_int_disable(__unused uint8_t rhport) -{ - assert(rhport == 0); - irq_set_enabled(USBCTRL_IRQ, false); +void dcd_int_disable(__unused uint8_t rhport) { + assert(rhport == 0); + irq_set_enabled(USBCTRL_IRQ, false); } -void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr) -{ +void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) { assert(rhport == 0); // Can't set device address in hardware until status xfer has complete @@ -448,8 +438,7 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr) hw_endpoint_xfer(0x80, NULL, 0); } -void dcd_remote_wakeup(__unused uint8_t rhport) -{ +void dcd_remote_wakeup(__unused uint8_t rhport) { pico_info("dcd_remote_wakeup %d\n", rhport); assert(rhport == 0); @@ -460,100 +449,88 @@ void dcd_remote_wakeup(__unused uint8_t rhport) } // disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(__unused uint8_t rhport) -{ +void dcd_disconnect(__unused uint8_t rhport) { (void) rhport; usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; } // connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(__unused uint8_t rhport) -{ +void dcd_connect(__unused uint8_t rhport) { (void) rhport; usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; } -void dcd_sof_enable(uint8_t rhport, bool en) -{ +void dcd_sof_enable(uint8_t rhport, bool en) { (void) rhport; _sof_enable = en; - if (en) - { + if (en) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; - }else - { + } +#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX + else { // Don't clear immediately if the SOF workaround is in use. // The SOF handler will conditionally disable the interrupt. -#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS; -#endif } +#endif } /*------------------------------------------------------------------*/ /* DCD Endpoint port *------------------------------------------------------------------*/ -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) -{ +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 ) - { + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS) { usb_hw->dev_addr_ctrl = (uint8_t) request->wValue; } } -bool dcd_edpt_open (__unused uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - assert(rhport == 0); - hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); - return true; +bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + assert(rhport == 0); + hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); + return true; } -void dcd_edpt_close_all (uint8_t rhport) -{ +void dcd_edpt_close_all(uint8_t rhport) { (void) rhport; // may need to use EP Abort reset_non_control_endpoints(); } -bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - assert(rhport == 0); - hw_endpoint_xfer(ep_addr, buffer, total_bytes); - return true; +bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + assert(rhport == 0); + hw_endpoint_xfer(ep_addr, buffer, total_bytes); + 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; - if ( tu_edpt_number(ep_addr) == 0 ) - { + if (tu_edpt_number(ep_addr) == 0) { // A stall on EP0 has to be armed so it can be cleared on the next setup packet - usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS; + usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS + : USB_EP_STALL_ARM_EP0_OUT_BITS; } - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); // stall and clear current pending buffer // may need to use EP_ABORT _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL); } -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; - if (tu_edpt_number(ep_addr)) - { - struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr); + if (tu_edpt_number(ep_addr)) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); // clear stall also reset toggle to DATA0, ready for next transfer ep->next_pid = 0; @@ -561,16 +538,13 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) } } -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - pico_trace("dcd_edpt_close %02x\r\n", ep_addr); - hw_endpoint_close(ep_addr); +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + pico_trace("dcd_edpt_close %02x\r\n", ep_addr); + hw_endpoint_close(ep_addr); } -void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) -{ +void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) { (void) rhport; dcd_rp2040_irq(); } diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 08aef9314..222dbbbf0 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -113,7 +113,7 @@ static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_en static void __tusb_irq_path_func(hw_handle_buff_status)(void) { uint32_t remaining_buffers = usb_hw->buf_status; - pico_trace("buf_status 0x%08x\n", remaining_buffers); + pico_trace("buf_status 0x%08lx\n", remaining_buffers); // Check EPX first uint bit = 0b1; @@ -325,10 +325,8 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep->wMaxPacketSize = wMaxPacketSize; ep->transfer_type = transfer_type; - pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), - ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type); - pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), - ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf); + pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type); + pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf); uint dpram_offset = hw_data_offset(ep->hw_data_buf); // Bits 0-5 should be 0 assert(!(dpram_offset & 0b111111)); @@ -343,7 +341,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB); } *ep->endpoint_control = ep_reg; - pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg); + pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg); ep->configured = true; if ( ep != &epx ) @@ -411,6 +409,16 @@ bool hcd_init(uint8_t rhport) return true; } +bool hcd_deinit(uint8_t rhport) { + (void) rhport; + + irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq); + reset_block(RESETS_RESET_USBCTRL_BITS); + unreset_block_wait(RESETS_RESET_USBCTRL_BITS); + + return true; +} + void hcd_port_reset(uint8_t rhport) { (void) rhport; diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c index a512dc34f..1ca711c77 100644 --- a/src/portable/raspberrypi/rp2040/rp2040_usb.c +++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c @@ -35,26 +35,18 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTOTYPE //--------------------------------------------------------------------+ - -// Direction strings for debug -const char *ep_dir_string[] = { - "out", - "in", -}; - -static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep); +static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep); #if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX - static bool e15_is_bulkin_ep(struct hw_endpoint *ep); - static bool e15_is_critical_frame_period(struct hw_endpoint *ep); + static bool e15_is_bulkin_ep(struct hw_endpoint* ep); + static bool e15_is_critical_frame_period(struct hw_endpoint* ep); #else #define e15_is_bulkin_ep(x) (false) #define e15_is_critical_frame_period(x) (false) #endif // if usb hardware is in host mode -TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) -{ +TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) { return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false; } @@ -62,8 +54,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) // Implementation //--------------------------------------------------------------------+ -void rp2040_usb_init(void) -{ +void rp2040_usb_init(void) { // Reset usb controller reset_block(RESETS_RESET_USBCTRL_BITS); unreset_block_wait(RESETS_RESET_USBCTRL_BITS); @@ -88,46 +79,33 @@ void rp2040_usb_init(void) TU_LOG2_INT(sizeof(hw_endpoint_t)); } -void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep) -{ +void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) { ep->active = false; ep->remaining_len = 0; ep->xferred_len = 0; ep->user_buf = 0; } -void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) -{ +void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask, + uint32_t or_mask) { uint32_t value = 0; - if ( and_mask ) - { + if (and_mask) { value = *ep->buffer_control & and_mask; } - if ( or_mask ) - { + if (or_mask) { value |= or_mask; - if ( or_mask & USB_BUF_CTRL_AVAIL ) - { - if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL ) - { - panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + if (or_mask & USB_BUF_CTRL_AVAIL) { + if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) { + panic("ep %02X was already available", ep->ep_addr); } *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL; - // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz) + // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control // Don't need delay in host mode as host is in charge -#if !CFG_TUH_ENABLED - __asm volatile ( - "b 1f\n" - "1: b 1f\n" - "1: b 1f\n" - "1: b 1f\n" - "1: b 1f\n" - "1: b 1f\n" - "1:\n" - : : : "memory"); -#endif + if ( !is_host_mode()) { + busy_wait_at_least_cycles(12); + } } } @@ -135,10 +113,9 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi } // prepare buffer, return buffer control -static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id) -{ +static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize); - ep->remaining_len = (uint16_t)(ep->remaining_len - buflen); + ep->remaining_len = (uint16_t) (ep->remaining_len - buflen); uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL; @@ -146,10 +123,9 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; ep->next_pid ^= 1u; - if ( !ep->rx ) - { + if (!ep->rx) { // Copy data from user buffer to hw buffer - memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen); + memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen); ep->user_buf += buflen; // Mark as full @@ -159,8 +135,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, // Is this the last buffer? Only really matters for host mode. Will trigger // the trans complete irq but also stop it polling. We only really care about // trans complete for setup packets being sent - if (ep->remaining_len == 0) - { + if (ep->remaining_len == 0) { buf_ctrl |= USB_BUF_CTRL_LAST; } @@ -170,8 +145,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, } // Prepare buffer control register value -void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) -{ +void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) { uint32_t ep_ctrl = *ep->endpoint_control; // always compute and start with buffer 0 @@ -186,8 +160,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) || (is_host && tu_edpt_number(ep->ep_addr) != 0); - if(ep->remaining_len && !force_single) - { + if (ep->remaining_len && !force_single) { // Use buffer 1 (double buffered) if there is still data // TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt) @@ -196,8 +169,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) // Set endpoint control double buffered bit if needed ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER; ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER; - }else - { + } else { // Single buffered since 1 is enough ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER); ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER; @@ -212,35 +184,28 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) _hw_endpoint_buffer_control_set_value32(ep, buf_ctrl); } -void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len) -{ +void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) { hw_endpoint_lock_update(ep, 1); - if ( ep->active ) - { + if (ep->active) { // TODO: Is this acceptable for interrupt packets? - TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\r\n", tu_edpt_number(ep->ep_addr), - ep_dir_string[tu_edpt_dir(ep->ep_addr)]); - + TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr); hw_endpoint_reset_transfer(ep); } // Fill in info now that we're kicking off the hw ep->remaining_len = total_len; - ep->xferred_len = 0; - ep->active = true; - ep->user_buf = buffer; + ep->xferred_len = 0; + ep->active = true; + ep->user_buf = buffer; - if ( e15_is_bulkin_ep(ep) ) - { + if (e15_is_bulkin_ep(ep)) { usb_hw_set->inte = USB_INTS_DEV_SOF_BITS; } - if ( e15_is_critical_frame_period(ep) ) - { + if (e15_is_critical_frame_period(ep)) { ep->pending = 1; - } else - { + } else { hw_endpoint_start_next_buffer(ep); } @@ -248,34 +213,30 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to } // sync endpoint buffer and return transferred bytes -static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id) -{ +static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) { uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep); - if (buf_id) buf_ctrl = buf_ctrl >> 16; + if (buf_id) buf_ctrl = buf_ctrl >> 16; uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK; - if ( !ep->rx ) - { + if (!ep->rx) { // We are continuing a transfer here. If we are TX, we have successfully // sent some data can increase the length we have sent assert(!(buf_ctrl & USB_BUF_CTRL_FULL)); - ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes); - }else - { + ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes); + } else { // If we have received some data, so can increase the length // we have received AFTER we have copied it to the user buffer at the appropriate offset assert(buf_ctrl & USB_BUF_CTRL_FULL); - memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes); - ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes); + 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; } // Short packet - if (xferred_bytes < ep->wMaxPacketSize) - { + if (xferred_bytes < ep->wMaxPacketSize) { pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes); // Reduce total length as this is last packet ep->remaining_len = 0; @@ -284,8 +245,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uin return xferred_bytes; } -static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep) -{ +static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) { // Update hw endpoint struct with info from hardware // after a buff status interrupt @@ -296,14 +256,11 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep uint16_t buf0_bytes = sync_ep_buffer(ep, 0); // sync buffer 1 if double buffered - if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS ) - { - if (buf0_bytes == ep->wMaxPacketSize) - { + if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) { + if (buf0_bytes == ep->wMaxPacketSize) { // sync buffer 1 if not short packet sync_ep_buffer(ep, 1); - }else - { + } else { // short packet on buffer 0 // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example // At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from @@ -335,14 +292,12 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep } // Returns true if transfer is complete -bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) -{ +bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) { hw_endpoint_lock_update(ep, 1); // Part way through a transfer - if (!ep->active) - { - panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + if (!ep->active) { + panic("Can't continue xfer on inactive ep %02X", ep->ep_addr); } // Update EP struct from hardware state @@ -350,21 +305,15 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) // Now we have synced our state with the hardware. Is there more data to transfer? // If we are done then notify tinyusb - if (ep->remaining_len == 0) - { - pico_trace("Completed transfer of %d bytes on ep %d %s\r\n", - ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]); + if (ep->remaining_len == 0) { + pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr); // Notify caller we are done so it can notify the tinyusb stack hw_endpoint_lock_update(ep, -1); return true; - } - else - { - if ( e15_is_critical_frame_period(ep) ) - { + } else { + if (e15_is_critical_frame_period(ep)) { ep->pending = 1; - } else - { + } else { hw_endpoint_start_next_buffer(ep); } } @@ -399,16 +348,14 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep) volatile uint32_t e15_last_sof = 0; // check if Errata 15 is needed for this endpoint i.e device bulk-in -static bool __tusb_irq_path_func(e15_is_bulkin_ep) (struct hw_endpoint *ep) -{ +static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) { return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN && ep->transfer_type == TUSB_XFER_BULK); } // check if we need to apply Errata 15 workaround : i.e // Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame -static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoint *ep) -{ +static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) { TU_VERIFY(e15_is_bulkin_ep(ep)); /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point. @@ -419,11 +366,10 @@ static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoi if (delta < 800 || delta > 998) { return false; } - TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), e15_last_sof); + TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), + e15_last_sof); return true; } -#endif - - +#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX #endif diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index 24edc30e7..50400d1f5 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -57,29 +57,22 @@ //--------------------------------------------------------------------+ // 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 */ } dcd_data_t; @@ -89,52 +82,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 +127,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 +135,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 +148,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 +246,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 +319,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; } @@ -844,7 +815,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 +915,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); @@ -1035,17 +1018,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/sony/cxd56/dcd_cxd56.c b/src/portable/sony/cxd56/dcd_cxd56.c index 6677891a5..41814370e 100644 --- a/src/portable/sony/cxd56/dcd_cxd56.c +++ b/src/portable/sony/cxd56/dcd_cxd56.c @@ -102,17 +102,25 @@ static int _dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_ usbdev = dev; usbdcd_driver.ep[0] = dev->ep0; + #ifdef EP_ALLOCREQ + // SDK v2 usbdcd_driver.req[0] = EP_ALLOCREQ(usbdcd_driver.ep[0]); - if (usbdcd_driver.req[0] != NULL) - { + if (usbdcd_driver.req[0] != NULL) { usbdcd_driver.req[0]->len = 64; usbdcd_driver.req[0]->buf = EP_ALLOCBUFFER(usbdcd_driver.ep[0], 64); - if (!usbdcd_driver.req[0]->buf) - { + if (!usbdcd_driver.req[0]->buf) { EP_FREEREQ(usbdcd_driver.ep[0], usbdcd_driver.req[0]); usbdcd_driver.req[0] = NULL; + return ENOMEM; } } + #else + // SDK v3 + usbdcd_driver.req[0] = usbdev_allocreq(usbdcd_driver.ep[0], 64); + if (usbdcd_driver.req[0] == NULL) { + return ENOMEM; + } + #endif usbdcd_driver.req[0]->callback = usbdcd_ep0incomplete; @@ -295,13 +303,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) } usbdcd_driver.req[epnum] = NULL; + + #ifdef EP_ALLOCREQ + // sdk v2 usbdcd_driver.req[epnum] = EP_ALLOCREQ(usbdcd_driver.ep[epnum]); - if (usbdcd_driver.req[epnum] != NULL) - { + if (usbdcd_driver.req[epnum] != NULL) { usbdcd_driver.req[epnum]->len = ep_mps; } - else - { + #else + // sdk v3 + usbdcd_driver.req[epnum] = usbdev_allocreq(usbdcd_driver.ep[epnum], ep_mps); + #endif + + if(usbdcd_driver.req[epnum] == NULL) { return false; } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 9c37f1f98..a26c66892 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -107,9 +107,9 @@ #include "device/dcd.h" #ifdef TUP_USBIP_FSDEV_STM32 - // Undefine to reduce the dependence on HAL - #undef USE_HAL_DRIVER - #include "portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h" +// Undefine to reduce the dependence on HAL +#undef USE_HAL_DRIVER +#include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h" #endif /***************************************************** @@ -119,17 +119,17 @@ // 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 +#define MAX_EP_COUNT 8U #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 +#define DCD_STM32_BTABLE_BASE 0U #endif #ifndef DCD_STM32_BTABLE_SIZE -# define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) +#define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) #endif /*************************************************** @@ -145,21 +145,18 @@ TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligne //--------------------------------------------------------------------+ // 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]; @@ -179,28 +176,25 @@ 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_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr); static void dcd_ep_ctr_handler(void); // 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 length, 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_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); //--------------------------------------------------------------------+ // Inline helper //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr) +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); @@ -214,7 +208,7 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr) // Controller API //--------------------------------------------------------------------+ -void dcd_init (uint8_t rhport) +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 */ @@ -222,43 +216,41 @@ void dcd_init (uint8_t rhport) /* 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 - { + for (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 - { + for (uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } USB->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 (uint32_t i = 0; i < 200; i++) { // should be a few us asm("NOP"); } USB->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 +#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; #endif USB->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 < STFSDEV_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); + pcd_set_endpoint(USB, i, 0u); } USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; dcd_handle_bus_reset(); // Enable pull-up if supported - if ( dcd_connect ) dcd_connect(rhport); + if (dcd_connect) { + dcd_connect(rhport); + } } // Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. @@ -267,14 +259,14 @@ void dcd_init (uint8_t rhport) // Disable internal D+ PU void dcd_disconnect(uint8_t rhport) { - (void) rhport; + (void)rhport; USB->BCDR &= ~(USB_BCDR_DPPU); } // Enable internal D+ PU void dcd_connect(uint8_t rhport) { - (void) rhport; + (void)rhport; USB->BCDR |= USB_BCDR_DPPU; } @@ -282,60 +274,54 @@ void dcd_connect(uint8_t rhport) // Disable internal D+ PU void dcd_disconnect(uint8_t rhport) { - (void) rhport; + (void)rhport; SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); } // Enable internal D+ PU void dcd_connect(uint8_t rhport) { - (void) rhport; + (void)rhport; SYSCFG->PMC |= SYSCFG_PMC_USB_PU; } #endif void dcd_sof_enable(uint8_t rhport, bool en) { - (void) rhport; - (void) en; + (void)rhport; + (void)en; - if (en) - { + if (en) { USB->CNTR |= USB_CNTR_SOFM; - } - else - { + } else { USB->CNTR &= ~USB_CNTR_SOFM; } } // Enable device interrupt -void dcd_int_enable (uint8_t rhport) +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 +#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) - { +// 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 + } else +#endif { NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn); NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); @@ -352,14 +338,14 @@ void dcd_int_enable (uint8_t rhport) 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 +#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); + NVIC_EnableIRQ(USB_DRD_FS_IRQn); #elif CFG_TUSB_MCU == OPT_MCU_STM32WB NVIC_EnableIRQ(USB_HP_IRQn); @@ -369,7 +355,7 @@ void dcd_int_enable (uint8_t rhport) NVIC_EnableIRQ(USB_FS_IRQn); #else - #error Unknown arch in USB driver +#error Unknown arch in USB driver #endif } @@ -378,24 +364,21 @@ void dcd_int_disable(uint8_t rhport) { (void)rhport; -#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \ - CFG_TUSB_MCU == OPT_MCU_STM32L4 +#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) - { +// 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 + } else +#endif { NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn); NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn); @@ -412,14 +395,14 @@ void dcd_int_disable(uint8_t rhport) 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 +#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); + NVIC_DisableIRQ(USB_DRD_FS_IRQn); #elif CFG_TUSB_MCU == OPT_MCU_STM32WB NVIC_DisableIRQ(USB_HP_IRQn); @@ -429,7 +412,7 @@ void dcd_int_disable(uint8_t rhport) NVIC_DisableIRQ(USB_FS_IRQn); #else - #error Unknown arch in USB driver +#error Unknown arch in USB driver #endif // CMSIS has a membar after disabling interrupts @@ -438,8 +421,8 @@ void dcd_int_disable(uint8_t rhport) // 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)rhport; + (void)dev_addr; // Respond with status dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); @@ -450,44 +433,35 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) void dcd_remote_wakeup(uint8_t rhport) { - (void) rhport; + (void)rhport; USB->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 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 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); - + for (uint32_t i = 0; i < STFSDEV_EP_COUNT; i++) { // Clear EP allocation status ep_alloc_status[i].ep_num = 0xFF; ep_alloc_status[i].ep_type = 0xFF; @@ -495,9 +469,11 @@ 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 = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT; + + dcd_edpt_open(0, &ep0OUT_desc); + dcd_edpt_open(0, &ep0IN_desc); USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. } @@ -513,30 +489,63 @@ static void dcd_ep_ctr_tx_handler(uint32_t wIstr) // 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) - { + if ((wEPRegVal & USB_EP_CTR_TX) == 0U) { return; } /* clear int flag */ pcd_clear_tx_ep_ctr(USB, EPindex); - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); - if((xfer->total_len != xfer->queued_len)) /* TX not complete */ - { - dcd_transmit_packet(xfer, EPindex); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + // 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; + + if (wEPRegVal & USB_EP_DTOG_TX) { + pcd_set_ep_tx_dbuf0_cnt(USB, EPindex, 0); + } else { + pcd_set_ep_tx_dbuf1_cnt(USB, EPindex, 0); + } } - else /* TX Complete */ - { + + if ((xfer->total_len != xfer->queued_len)) { + dcd_transmit_packet(xfer, EPindex); + } else { dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); } } // 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 + /* 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: + * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses + * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. + * Workaround: + * - 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 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 3 cycles (1 for sub, jump, and compare) + } +#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; @@ -545,96 +554,83 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr) // 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) - { + if ((wEPRegVal & USB_EP_CTR_RX) == 0U) { return; } - if((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */ - { + 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. - { + // Setup packet should always be 8 bytes. If not, ignore it, and try again. + if (count == 8) { // 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); + 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); + 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); + dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB, EPindex), 8); + dcd_event_setup_received(0, (uint8_t *)userMemBuf, true); #endif } - } - else - { + } else { + // Clear RX CTR interrupt flag + if (ep_addr != 0u) { + pcd_clear_rx_ep_ctr(USB, EPindex); + } + uint32_t count; + uint16_t addr; /* 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); + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + if (wEPRegVal & USB_EP_DTOG_RX) { + count = pcd_get_ep_dbuf0_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf0_address(USB, EPindex); + } else { + count = pcd_get_ep_dbuf1_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf1_address(USB, EPindex); + } } else { count = pcd_get_ep_rx_cnt(USB, EPindex); + addr = pcd_get_ep_rx_address(USB, EPindex); } TU_ASSERT(count <= xfer->max_packet_size, /**/); - // Clear RX CTR interrupt flag - if(ep_addr != 0u) - { - pcd_clear_rx_ep_ctr(USB, EPindex); - } - - if (count != 0U) - { - uint16_t addr = pcd_get_ep_rx_address(USB, EPindex); - - if (xfer->ff) - { + if (count != 0U) { + if (xfer->ff) { dcd_read_packet_memory_ff(xfer->ff, addr, count); - } - else - { + } else { dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); } xfer->queued_len = (uint16_t)(xfer->queued_len + count); } - if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) - { - /* RX COMPLETE */ + if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { + // all bytes received or short packet 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); - } else { - pcd_set_ep_rx_bufsize(USB, EPindex,remaining); - } - - 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); + } else { + /* Set endpoint active again for receiving more data. + * Note that isochronous endpoints stay active always */ + if ((wEPRegVal & USB_EP_TYPE_MASK) != USB_EP_ISOCHRONOUS) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t cnt = tu_min16(remaining, xfer->max_packet_size); + pcd_set_ep_rx_cnt(USB, EPindex, cnt); } + pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); } } // 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); + if (ep_addr == 0u) { + // Always be prepared for a status packet... + pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); pcd_clear_rx_ep_ctr(USB, EPindex); } } @@ -644,40 +640,38 @@ static void dcd_ep_ctr_handler(void) uint32_t wIstr; /* stay in loop while pending interrupts */ - while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) - { - - if ((wIstr & USB_ISTR_DIR) == 0U) /* TX/IN */ - { + while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { + if ((wIstr & USB_ISTR_DIR) == 0U) { + /* TX/IN */ dcd_ep_ctr_tx_handler(wIstr); - } - else /* RX/OUT*/ - { + } else { + /* RX/OUT*/ dcd_ep_ctr_rx_handler(wIstr); } } } -void dcd_int_handler(uint8_t rhport) { +void dcd_int_handler(uint8_t rhport) +{ - (void) 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 ) + // 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) { + 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_RESET) { + if (int_status & USB_ISTR_RESET) { // USBRST is start of reset. USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; dcd_handle_bus_reset(); @@ -685,15 +679,13 @@ void dcd_int_handler(uint8_t rhport) { return; // Don't do the rest of the things here; perhaps they've been cleared? } - if (int_status & USB_ISTR_CTR) - { + 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) - { + if (int_status & USB_ISTR_WKUP) { USB->CNTR &= ~USB_CNTR_LPMODE; USB->CNTR &= ~USB_CNTR_FSUSP; @@ -701,8 +693,7 @@ void dcd_int_handler(uint8_t rhport) { 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. */ @@ -715,13 +706,11 @@ void dcd_int_handler(uint8_t rhport) { dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } - if(int_status & USB_ISTR_ESOF) { - if(remoteWakeCountdown == 1u) - { + if (int_status & USB_ISTR_ESOF) { + if (remoteWakeCountdown == 1u) { USB->CNTR &= ~USB_CNTR_RESUME; } - if(remoteWakeCountdown > 0u) - { + if (remoteWakeCountdown > 0u) { remoteWakeCountdown--; } USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; @@ -734,15 +723,14 @@ 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 dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { - (void) rhport; + (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; + 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; @@ -750,41 +738,13 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re } } -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; - } -} - /*** * 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 length, 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; - } - // Ensure allocated buffer is aligned #ifdef FSDEV_BUS_32BIT length = (length + 3) & ~0x03; @@ -792,79 +752,44 @@ static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length) length = (length + 1) & ~0x01; #endif - open_ep_count++; - - uint16_t addr = ep_buf_ptr; + uint32_t addr = ep_buf_ptr; ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer - // Verify no overflow - TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); + if (dbuf) { + addr |= ((uint32_t)ep_buf_ptr) << 16; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + } - epXferCtl->pma_ptr = addr; - epXferCtl->pma_alloc_size = length; - //TU_LOG1("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr); + // Verify packet buffer is not overflowed + TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); 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 < STFSDEV_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; @@ -879,121 +804,79 @@ 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); - - 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; - - return; - } - } - } -} - // The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, // so I'm using the #define from HAL here, instead. -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) +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); + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, p_endpoint_desc->bmAttributes.xfer); + uint8_t const dir = tu_edpt_dir(ep_addr); 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; TU_ASSERT(ep_idx < STFSDEV_EP_COUNT); - TU_ASSERT(buffer_size <= 1024); + TU_ASSERT(buffer_size <= 64); // 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; + switch (p_endpoint_desc->bmAttributes.xfer) { + case TUSB_XFER_CONTROL: + wType = USB_EP_CONTROL; + break; + case TUSB_XFER_BULK: + wType = USB_EP_CONTROL; + break; - case TUSB_XFER_INTERRUPT: - wType = USB_EP_INTERRUPT; - break; + case TUSB_XFER_INTERRUPT: + wType = USB_EP_INTERRUPT; + break; - default: - TU_ASSERT(false); + default: + // Note: ISO endpoint should use alloc / active functions + TU_ASSERT(false); } 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); + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); - /* 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); + /* Create a packet memory buffer area. */ + pma_addr = dcd_pma_alloc(buffer_size, false); - if( (dir == TUSB_DIR_IN) || (wType == USB_EP_ISOCHRONOUS) ) - { + if (dir == TUSB_DIR_IN) { pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size); + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); pcd_clear_tx_dtog(USB, ep_idx); - } - - if( (dir == TUSB_DIR_OUT) || (wType == USB_EP_ISOCHRONOUS) ) - { + } else { pcd_set_ep_rx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); pcd_clear_rx_dtog(USB, ep_idx); } - /* 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); - } - } - - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size; - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx = ep_idx; + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; return true; } -void dcd_edpt_close_all (uint8_t rhport) +void dcd_edpt_close_all(uint8_t rhport) { - (void) rhport; - // TODO implement dcd_edpt_close_all() + (void)rhport; + + for (uint32_t i = 1; i < STFSDEV_EP_COUNT; i++) { + // Reset endpoint + pcd_set_endpoint(USB, 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; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * MAX_EP_COUNT + 2 * CFG_TUD_ENDPOINT0_SIZE; } /** @@ -1003,223 +886,203 @@ void dcd_edpt_close_all (uint8_t rhport) * * 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(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); + 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); + uint8_t const dir = tu_edpt_dir(ep_addr); - if(dir == TUSB_DIR_IN) - { + if (dir == TUSB_DIR_IN) { pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); - } - else - { + } else { pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); } - - dcd_ep_free(ep_addr); - - dcd_pma_free(ep_addr); } 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_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. */ +#if FSDEV_PMA_SIZE > 1024u + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr >> 16; +#else + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr; +#endif + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_address(USB, ep_idx, pma_addr2); pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); - pcd_set_ep_tx_address(USB, ep_idx, pma_addr); - pcd_set_ep_rx_address(USB, ep_idx, pma_addr); + xfer_ctl_ptr(ep_addr)->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 *p_endpoint_desc) { (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); + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = xfer_ctl_ptr(ep_addr)->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); - const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); - /* 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); - } + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); - 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); + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); - 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); - xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size; + if (dir == TUSB_DIR_IN) { + pcd_rx_dtog(USB, ep_idx); + } else { + pcd_tx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; 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) +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 - { + if (len > xfer->max_packet_size) { len = xfer->max_packet_size; } uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); - uint16_t addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); + bool const is_iso = (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; + uint16_t addr_ptr; - if (xfer->ff) - { - dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); - } - else - { - dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); + if (is_iso) { + if (ep_reg & USB_EP_DTOG_TX) { + addr_ptr = pcd_get_ep_dbuf1_address(USB, ep_ix); + pcd_set_ep_tx_dbuf1_cnt(USB, ep_ix, len); + } else { + addr_ptr = pcd_get_ep_dbuf0_address(USB, ep_ix); + pcd_set_ep_tx_dbuf0_cnt(USB, ep_ix, len); + } + } else { + addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); + pcd_set_ep_tx_cnt(USB, ep_ix, 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); + if (xfer->ff) { + dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); } else { - pcd_set_ep_tx_cnt(USB, ep_ix, len); + dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); } + xfer->queued_len = (uint16_t)(xfer->queued_len + len); + dcd_int_disable(0); pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); + 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_addr) { - (void) rhport; + (void)rhport; - xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr); + 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); + 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 ) - { + if (dir == TUSB_DIR_IN) { + dcd_transmit_packet(xfer, ep_idx); + } else { // 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 (ep_idx == 0 && xfer->buffer == NULL) { + xfer->buffer = (uint8_t *)_setup_packet; } - if(total_bytes > xfer->max_packet_size) - { - pcd_set_ep_rx_bufsize(USB,ep_idx,xfer->max_packet_size); + uint32_t cnt = (uint32_t ) tu_min16(xfer->total_len, xfer->max_packet_size); + uint16_t ep_reg = pcd_get_endpoint(USB, ep_idx); + + if ((ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_dbuf0_cnt(USB, ep_idx, cnt); + pcd_set_ep_rx_dbuf1_cnt(USB, ep_idx, cnt); } else { - pcd_set_ep_rx_bufsize(USB,ep_idx,total_bytes); + pcd_set_ep_rx_cnt(USB, ep_idx, cnt); } + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); } - else // IN - { - dcd_transmit_packet(xfer,ep_idx); - } + return true; } -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { - (void) rhport; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); - 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); + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + return edpt_xfer(rhport, ep_addr); +} + +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); 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_addr); } -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); + 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); + uint8_t const dir = tu_edpt_dir(ep_addr); - if (dir == TUSB_DIR_IN) - { // IN + if (dir == TUSB_DIR_IN) { pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); - } - else - { // OUT + } else { pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); } } -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); + 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); + 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) { + 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); } /* 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) { + } 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. */ @@ -1230,8 +1093,8 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) #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); + const uint8_t *srcVal = src; + volatile uint32_t *dst32 = (volatile uint32_t *)(USB_PMAADDR + dst); for (uint32_t n = wNBytes / 4; n > 0; --n) { *dst32++ = tu_unaligned_read32(srcVal); @@ -1239,18 +1102,15 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui } wNBytes = wNBytes & 0x03; - if (wNBytes) - { + if (wNBytes) { uint32_t wrVal = *srcVal; wNBytes--; - if (wNBytes) - { + if (wNBytes) { wrVal |= *++srcVal << 8; wNBytes--; - if (wNBytes) - { + if (wNBytes) { wrVal |= *++srcVal << 16; } } @@ -1263,20 +1123,20 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui #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 - */ + * @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; + const uint8_t *srcVal; // The GCC optimizer will combine access to 32-bit sizes if we let it. Force // it volatile so that it won't do that. @@ -1285,18 +1145,16 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui srcVal = src; pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; - while (n--) - { + while (n--) { temp1 = (uint16_t)*srcVal; srcVal++; - temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ; + temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)); *pdwVal = temp2; pdwVal += FSDEV_PMA_STRIDE; srcVal++; } - if (wNBytes) - { + if (wNBytes) { temp1 = *srcVal; *pdwVal = temp1; } @@ -1306,42 +1164,39 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, ui #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 @@ -1350,32 +1205,29 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN // Copy rest of wrapped byte if (wCnt) - dcd_write_packet_memory(dst, info.ptr_wrap, 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); } @@ -1389,8 +1241,8 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN #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); + 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++); @@ -1398,20 +1250,17 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t } wNBytes = wNBytes & 0x03; - if (wNBytes) - { + if (wNBytes) { uint32_t rdVal = *src32; *dstVal = tu_u32_byte0(rdVal); wNBytes--; - if (wNBytes) - { + if (wNBytes) { *++dstVal = tu_u32_byte1(rdVal); wNBytes--; - if (wNBytes) - { + if (wNBytes) { *++dstVal = tu_u32_byte2(rdVal); } } @@ -1421,11 +1270,11 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t } #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 - */ + * @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; @@ -1435,18 +1284,16 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t uint32_t temp; pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; - uint8_t *dstVal = (uint8_t*)dst; + uint8_t *dstVal = (uint8_t *)dst; - while (n--) - { + while (n--) { temp = *pdwVal; pdwVal += FSDEV_PMA_STRIDE; *dstVal++ = ((temp >> 0) & 0xFF); *dstVal++ = ((temp >> 8) & 0xFF); } - if (wNBytes & 0x01) - { + if (wNBytes & 0x01) { temp = *pdwVal; pdwVal += FSDEV_PMA_STRIDE; *dstVal++ = ((temp >> 0) & 0xFF); @@ -1456,31 +1303,29 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t #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]; @@ -1488,15 +1333,13 @@ 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 @@ -1504,32 +1347,29 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB 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_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h index 3f4db985d..7992f34a1 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h @@ -1,30 +1,32 @@ -/** - * Copyright(c) 2016 STMicroelectronics - * Copyright(c) N Conrad - * - * 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. - * - */ +/* + * 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. @@ -113,6 +115,11 @@ #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 @@ -138,7 +145,6 @@ #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) @@ -182,27 +188,23 @@ 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) -{ +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) -{ +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) -{ +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) -{ +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; @@ -213,9 +215,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t si return numblocks * blocksize; } -/* SetENDPOINT */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) -{ +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); @@ -226,7 +226,6 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, ui #endif } -/* GetENDPOINT */ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { #ifdef FSDEV_BUS_32BIT (void) USBx; @@ -237,8 +236,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx return *reg; } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) -{ +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; @@ -246,20 +244,19 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint pcd_set_endpoint(USBx, bEpIdx, regVal); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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) -{ +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; @@ -267,22 +264,21 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, pcd_set_endpoint(USBx, bEpIdx, regVal); } -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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) -{ +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; @@ -292,8 +288,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USB #endif } -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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; @@ -303,6 +298,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USB #endif } +#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt +#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt + /** * @brief Sets address in an endpoint register. * @param USBx USB peripheral instance register address. @@ -310,8 +308,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USB * @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) -{ +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; @@ -319,8 +316,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, 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) -{ +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 ; @@ -329,8 +325,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * #endif } -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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; @@ -339,8 +334,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * #endif } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) -{ +#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address +#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address + +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); @@ -349,8 +346,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USB #endif } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) -{ +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); @@ -359,8 +355,10 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USB #endif } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) -{ +#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address +#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address + +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); @@ -370,8 +368,9 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, u #endif } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) -{ +#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_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); @@ -381,8 +380,8 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, u #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) -{ +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; @@ -393,8 +392,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDe #endif } -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) -{ +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. */ @@ -408,16 +406,16 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, 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) -{ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(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) -{ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); } +#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt + /** * @brief sets the status for tx transfer (bits STAT_TX[1:0]). * @param USBx USB peripheral instance register address. @@ -425,8 +423,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USB * @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) -{ +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; @@ -443,7 +440,7 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; pcd_set_endpoint(USBx, bEpIdx, regVal); -} /* pcd_set_ep_tx_status */ +} /** * @brief sets the status for rx transfer (bits STAT_TX[1:0]) @@ -453,31 +450,27 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx * @retval None */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) -{ +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) - { + if((USB_EPRX_DTOG1 & wState)!= 0U) { regVal ^= USB_EPRX_DTOG1; } /* toggle second bit ? */ - if((USB_EPRX_DTOG2 & wState)!= 0U) - { + 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); -} /* pcd_set_ep_rx_status */ +} -TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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); -} /* pcd_get_ep_rx_status */ +} /** @@ -486,16 +479,14 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * * @param bEpIdx Endpoint Number. * @retval None */ -TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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) -{ +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; @@ -508,21 +499,16 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32 * @param bEpIdx Endpoint Number. * @retval None */ - -TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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) - { + 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) -{ +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) - { + if((regVal & USB_EP_DTOG_TX) != 0) { pcd_tx_dtog(USBx,bEpIdx); } } @@ -533,17 +519,15 @@ TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, * @param bEpIdx Endpoint Number. * @retval None */ - -TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) -{ +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) -{ + +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; 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 8163ba8e1..c9580655f 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -97,28 +97,170 @@ static uint16_t ep0_pending[2]; // Index determines direction as t // TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) -static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size) // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by static bool _sof_en; -// Calculate the RX FIFO size according to recommendations from reference manual +// Calculate the RX FIFO size according to minimum recommendations from reference manual +// RxFIFO = (5 * number of control endpoints + 8) + +// ((largest USB packet used / 4) + 1 for status information) + +// (2 * number of OUT endpoints) + 1 for Global NAK +// with number of control endpoints = 1 we have +// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints +// we double the largest USB packet size to be able to hold up to 2 packets 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; } -static void update_grxfsiz(uint8_t rhport) { +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; + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - // Determine largest EP size for RX FIFO - uint16_t max_epsize = 0; - for (uint8_t epnum = 0; epnum < ep_count; epnum++) { - max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size); + TU_ASSERT(epnum < ep_count); + + uint16_t fifo_size = tu_div_ceil(packet_size, 4); + + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture + // + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO 0 | + // --------------- (320 or 1024) - 16 + // | IN FIFO 1 | + // --------------- (320 or 1024) - 16 - x + // | . . . . | + // --------------- (320 or 1024) - 16 - x - y - ... - z + // | IN FIFO MAX | + // --------------- + // | FREE | + // --------------- GRXFSIZ + // | OUT FIFO | + // | ( Shared ) | + // --------------- 0 + // + // In FIFO is allocated by following rules: + // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". + if (dir == TUSB_DIR_OUT) { + // Calculate required size of RX FIFO + uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count); + + // If size_rx needs to be extended check if possible and if so enlarge it + if (dwc2->grxfsiz < sz) { + TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4); + + // Enlarge RX FIFO + 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 %" PRIu32, fifo_size * 4, + _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); + + // DIEPTXF starts at FIFO #1. + // Both TXFD and TXSA are in unit of 32-bit words. + dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | + (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + } + + return true; +} + +static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); + xfer->max_size = tu_edpt_packet_size(p_endpoint_desc); + xfer->interval = p_endpoint_desc->bInterval; + + // USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints. + uint32_t const dxepctl = (1 << DOEPCTL_USBAEP_Pos) | + (p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | + (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | + (xfer->max_size << DOEPCTL_MPSIZ_Pos); + + if (dir == TUSB_DIR_OUT) { + dwc2->epout[epnum].doepctl = dxepctl; + dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); + } else { + dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); + dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); } +} + +static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); - // Update size of RX FIFO - dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count); + if (dir == TUSB_DIR_IN) { + dwc2_epin_t* epin = dwc2->epin; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) { + epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); + } else { + // Stop transmitting packets and NAK IN xfers. + epin[epnum].diepctl |= DIEPCTL_SNAK; + while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {} + + // Disable the endpoint. + epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); + while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {} + + epin[epnum].diepint = DIEPINT_EPDISD; + } + + // Flush the FIFO, and wait until we have confirmed it cleared. + fifo_flush_tx(dwc2, epnum); + } else { + dwc2_epout_t* epout = dwc2->epout; + + // Only disable currently enabled non-control endpoint + if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) { + epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0; + } else { + // Asserting GONAK is required to STALL an OUT endpoint. + // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt + // anyway, and it can't be cleared by user code. If this while loop never + // finishes, we have bigger problems than just the stack. + dwc2->dctl |= DCTL_SGONAK; + while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {} + + // Ditto here- disable the endpoint. + epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); + while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {} + + epout[epnum].doepint = DOEPINT_EPDISD; + + // Allow other OUT endpoints to keep receiving. + dwc2->dctl |= DCTL_CGONAK; + } + } } // Start of Bus Reset @@ -127,7 +269,6 @@ static void bus_reset(uint8_t rhport) { uint8_t const ep_count = _dwc2_controller[rhport].ep_count; tu_memclr(xfer_status, sizeof(xfer_status)); - _out_ep_closed = false; _sof_en = false; @@ -139,7 +280,17 @@ static void bus_reset(uint8_t rhport) { dwc2->epout[n].doepctl |= DOEPCTL_SNAK; } - // 2. Set up interrupt mask + // 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; + } + } + + fifo_flush_tx(dwc2, 0x10); // all tx fifo + fifo_flush_rx(dwc2); + + // 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; @@ -269,18 +420,16 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c /* Controller API *------------------------------------------------------------------*/ #if CFG_TUSB_DEBUG >= DWC2_DEBUG - void print_dwc2_info(dwc2_regs_t* dwc2) { // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 // use dwc2_info.py/md for bit-field value and comparison with other ports 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 static void reset_core(dwc2_regs_t* dwc2) { @@ -298,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 } @@ -447,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; @@ -462,11 +610,12 @@ void dcd_init(uint8_t rhport) { dwc2->gotgint |= int_mask; // Required as part of core initialization. - // TODO: How should mode mismatch be handled? It will cause - // the core to stop working/require reset. - dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_MMISM | GINTMSK_RXFLVLM | + 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; @@ -547,84 +696,8 @@ void dcd_sof_enable(uint8_t rhport, bool en) { *------------------------------------------------------------------*/ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < ep_count); - - xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - xfer->interval = desc_edpt->bInterval; - - uint16_t const fifo_size = tu_div_ceil(xfer->max_size, 4); - - if (dir == TUSB_DIR_OUT) { - // Calculate required size of RX FIFO - uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count); - - // If size_rx needs to be extended check if possible and if so enlarge it - if (dwc2->grxfsiz < sz) { - TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4); - - // Enlarge RX FIFO - dwc2->grxfsiz = sz; - } - - dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) | - (desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << DOEPCTL_MPSIZ_Pos); - - dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); - } else { - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // In FIFO is allocated by following rules: - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". - - // 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, - _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); - - // DIEPTXF starts at FIFO #1. - // Both TXFD and TXSA are in unit of 32-bit words. - dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | - (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); - - dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) | - (epnum << DIEPCTL_TXFNUM_Pos) | - (desc_edpt->bmAttributes.xfer << DIEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DIEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << DIEPCTL_MPSIZ_Pos); - - dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); - } - + TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); + edpt_activate(rhport, desc_edpt); return true; } @@ -638,16 +711,39 @@ 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) { + TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size)); + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { + // Disable EP to clear potential incomplete transfers + edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false); + + edpt_activate(rhport, p_endpoint_desc); + + return true; } bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { @@ -707,89 +803,12 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t return true; } -static void dcd_edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - dwc2_epin_t* epin = dwc2->epin; - - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA)) { - epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0); - } else { - // Stop transmitting packets and NAK IN xfers. - epin[epnum].diepctl |= DIEPCTL_SNAK; - while ((epin[epnum].diepint & DIEPINT_INEPNE) == 0) {} - - // Disable the endpoint. - epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0); - while ((epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0) {} - - epin[epnum].diepint = DIEPINT_EPDISD; - } - - // 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) {} - } else { - dwc2_epout_t* epout = dwc2->epout; - - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA)) { - epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0; - } else { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // anyway, and it can't be cleared by user code. If this while loop never - // finishes, we have bigger problems than just the stack. - dwc2->dctl |= DCTL_SGONAK; - while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {} - - // Ditto here- disable the endpoint. - epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0); - while ((epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0) {} - - epout[epnum].doepint = DOEPINT_EPDISD; - - // Allow other OUT endpoints to keep receiving. - dwc2->dctl |= DCTL_CGONAK; - } - } -} - -/** - * Close an endpoint. - */ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_disable(rhport, ep_addr, false); - - // Update max_size - xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation - - if (dir == TUSB_DIR_IN) { - uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos; - uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos; - - // For now only the last opened endpoint can be closed without fuss. - TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx,); - _allocated_fifo_words_tx -= fifo_size; - } else { - _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty - } + edpt_disable(rhport, ep_addr, false); } void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { - dcd_edpt_disable(rhport, ep_addr, true); + edpt_disable(rhport, ep_addr, true); } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -1130,18 +1149,16 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gotgint = otg_int; } - if (int_status & GINTSTS_SOF) { - dwc2->gotgint = GINTSTS_SOF; + 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. @@ -1154,15 +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); - - // Manage RX FIFO size - if (_out_ep_closed) { - update_grxfsiz(rhport); - - // Disable flag - _out_ep_closed = false; - } + } while(dwc2->gintsts & GINTSTS_RXFLVL); dwc2->gintmsk |= GINTMSK_RXFLVLM; } 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/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index 1f1c0b876..68e2179e9 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -135,6 +135,16 @@ void dcd_remote_wakeup(uint8_t rhport) (void) rhport; } +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); + } +} + void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { (void)rhport; @@ -332,7 +342,10 @@ void dcd_int_handler(uint8_t rhport) { xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp)); - if (rx_token == PID_OUT) { + if (rx_token == PID_SOF) { + dcd_event_sof(rhport, USBHSD->FRAME_NO, true); + + } else if (rx_token == PID_OUT) { uint16_t rx_len = USBHSD->RX_LEN; receive_packet(xfer, rx_len); |
