summaryrefslogtreecommitdiff
path: root/src/portable/synopsys
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2025-11-19 22:05:07 +0100
committerHiFiPhile <[email protected]>2025-11-19 22:05:07 +0100
commit85adf694e659bde41b34e2f585cb2db552279064 (patch)
tree6445badf0b427556fd47fb3d7b682bb8b439039e /src/portable/synopsys
parent2f0a35f21af4c09154d6b4b5b118325af8990e51 (diff)
parent790c7a0d7a6099e88a0bf6bbc341504c8bdaa974 (diff)
Merge remote-tracking branch 'tinyusb/master' into copilot/fix-dcd-edpt-xfer-issue
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'src/portable/synopsys')
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c135
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h4
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h10
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c215
4 files changed, 262 insertions, 102 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 22a25fe40..e99cd29c6 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -51,6 +51,7 @@ typedef struct {
uint16_t total_len;
uint16_t max_size;
uint8_t interval;
+ uint8_t iso_retry; // ISO retry counter
} xfer_ctl_t;
// This variable is modified from ISR context, so it must be protected by critical section
@@ -261,7 +262,13 @@ static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint
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;
+
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
+ if (dsts.enum_speed == DCFG_SPEED_HIGH) {
+ xfer->interval = 1 << (p_endpoint_desc->bInterval - 1);
+ } else {
+ xfer->interval = p_endpoint_desc->bInterval;
+ }
// Endpoint control
dwc2_depctl_t depctl = {.value = 0};
@@ -332,6 +339,40 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
+static uint16_t epin_write_tx_fifo(uint8_t rhport, uint8_t epnum) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2_dep_t* const epin = &dwc2->ep[0][epnum];
+ xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
+
+ dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
+ const uint16_t remain_packets = tsiz.packet_count;
+
+ uint16_t total_bytes_written = 0;
+ // Process every single packet (only whole packets can be written to fifo)
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ tsiz.value = epin->tsiz;
+ const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
+
+ // Check if dtxfsts has enough space available
+ if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
+ }
+
+ // Push packet to Tx-FIFO
+ if (xfer->ff) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
+ total_bytes_written += xact_bytes;
+ } else {
+ dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
+ xfer->buffer += xact_bytes;
+ total_bytes_written += xact_bytes;
+ }
+ }
+ return total_bytes_written;
+}
+
// Since this function returns void, it is not possible to return a boolean success message
// We must make sure that this function is not called when the EP is disabled
// Must be called from critical section
@@ -345,7 +386,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
// EP0 is limited to one packet per xfer
if (epnum == 0) {
- total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size);
+ total_bytes = tu_min16(_dcd_data.ep0_pending[dir], CFG_TUD_ENDPOINT0_SIZE);
_dcd_data.ep0_pending[dir] -= total_bytes;
num_packets = 1;
} else {
@@ -366,7 +407,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
dwc2_depctl_t depctl = {.value = dep->ctl};
depctl.clear_nak = 1;
depctl.enable = 1;
- if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
+ if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS) {
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
const uint32_t odd_now = dsts.frame_number & 1u;
if (odd_now != 0) {
@@ -383,12 +424,21 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
}
dep->diepdma = (uintptr_t) xfer->buffer;
dep->diepctl = depctl.value; // enable endpoint
+ // Advance buffer pointer for EP0
+ if (epnum == 0) {
+ xfer->buffer += total_bytes;
+ }
} else {
dep->diepctl = depctl.value; // enable endpoint
- // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
if (dir == TUSB_DIR_IN && total_bytes != 0) {
- dwc2->diepempmsk |= (1u << epnum); //-V629
+ const uint16_t xferred_bytes = epin_write_tx_fifo(rhport, epnum);
+
+ // Enable TXFE interrupt if there are still data to be sent
+ // EP0 only sends one packet at a time, so no need to check for EP0
+ if ((epnum != 0) && (xfer->total_len - xferred_bytes > 0)) {
+ dwc2->diepempmsk |= (1u << epnum);
+ }
}
}
}
@@ -601,6 +651,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
xfer->buffer = buffer;
xfer->ff = NULL;
xfer->total_len = total_bytes;
+ xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
// EP0 can only handle one packet
if (epnum == 0) {
@@ -639,6 +690,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
+ xfer->iso_retry = xfer->interval; // Reset ISO retry counter to interval value
// Schedule packets to be sent within interrupt
// TODO xfer fifo may only available for slave mode
@@ -741,7 +793,7 @@ static void handle_bus_reset(uint8_t rhport) {
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
+ dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
}
static void handle_enum_done(uint8_t rhport) {
@@ -838,11 +890,9 @@ static void handle_rxflvl_irq(uint8_t rhport) {
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
xfer->total_len -= tsiz.xfer_size;
if (epnum == 0) {
- xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
_dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
}
}
-
break;
}
@@ -902,32 +952,10 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
// - 64 bytes or
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
if (diepint_bm.txfifo_empty && tu_bit_test(dwc2->diepempmsk, epnum)) {
- dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
- const uint16_t remain_packets = tsiz.packet_count;
-
- // Process every single packet (only whole packets can be written to fifo)
- for (uint16_t i = 0; i < remain_packets; i++) {
- tsiz.value = epin->tsiz;
- const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
- const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
-
- // Check if dtxfsts has enough space available
- if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
- break;
- }
-
- // Push packet to Tx-FIFO
- if (xfer->ff != NULL) {
- volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
- } else {
- dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
- xfer->buffer += xact_bytes;
- }
- }
+ epin_write_tx_fifo(rhport, epnum);
// Turn off TXFE if all bytes are written.
- tsiz.value = epin->tsiz;
+ dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
if (tsiz.xfer_size == 0) {
dwc2->diepempmsk &= ~(1u << epnum);
}
@@ -1040,6 +1068,43 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
}
}
+static void handle_incomplete_iso_in(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const dwc2_dsts_t dsts = {.value = dwc2->dsts};
+ const uint32_t odd_now = dsts.frame_number & 1u;
+
+ // Loop over all IN endpoints
+ const uint8_t ep_count = dwc2_ep_count(dwc2);
+ for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
+ dwc2_dep_t *epin = &dwc2->epin[epnum];
+ dwc2_depctl_t depctl = {.value = epin->diepctl};
+ // Read DSTS and DIEPCTLn for all isochronous endpoints. If the current EP is enabled and the read value of
+ // DSTS.SOFFN is the targeted uframe number for this EP, then this EP has an incomplete transfer.
+ if (depctl.enable && depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && depctl.dpid_iso_odd == odd_now) {
+ xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
+ if (xfer->iso_retry > 0) {
+ xfer->iso_retry--;
+ // Restart ISO transfe: re-write TSIZ and CTL
+ dwc2_ep_tsize_t deptsiz = {.value = 0};
+ deptsiz.xfer_size = xfer->total_len;
+ deptsiz.packet_count = tu_div_ceil(xfer->total_len, xfer->max_size);
+ epin->tsiz = deptsiz.value;
+
+ if (odd_now) {
+ depctl.set_data0_iso_even = 1;
+ } else {
+ depctl.set_data1_iso_odd = 1;
+ }
+ epin->diepctl = depctl.value;
+ } else {
+ // too many retries, give up
+ edpt_disable(rhport, epnum | TUSB_DIR_IN_MASK, false);
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, 0, XFER_RESULT_FAILED, true);
+ }
+ }
+ }
+}
+
/* Interrupt Hierarchy
DIEPINT DIEPINT
\ /
@@ -1139,6 +1204,12 @@ void dcd_int_handler(uint8_t rhport) {
// IEPINT bit read-only, clear using DIEPINTn
handle_ep_irq(rhport, TUSB_DIR_IN);
}
+
+ // Incomplete isochronous IN transfer interrupt handling.
+ if (gintsts & GINTSTS_IISOIXFR) {
+ dwc2->gintsts = GINTSTS_IISOIXFR;
+ handle_incomplete_iso_in(rhport);
+ }
}
#if CFG_TUD_TEST_MODE
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
index dc204f578..428304ba9 100644
--- a/src/portable/synopsys/dwc2/dwc2_common.h
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -94,13 +94,13 @@ void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) {
// flush TX fifo and wait for it cleared
dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos);
- while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+ while (0 != (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk)) {}
}
TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
// flush RX fifo and wait for it cleared
dwc2->grstctl = GRSTCTL_RXFFLSH;
- while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+ while (0 != (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk)) {}
}
void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len);
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index adcc579e3..7693ce02a 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -92,6 +92,16 @@ enum {
};
enum {
+ GUSBCFG_PHYSEL_HIGHSPEED = 0,
+ GUSBCFG_PHYSEL_FULLSPEED = 1,
+};
+
+enum {
+ GUSBCFG_PHYHS_UTMI = 0,
+ GUSBCFG_PHYHS_ULPI = 1,
+};
+
+enum {
GHWCFG2_OPMODE_HNP_SRP = 0,
GHWCFG2_OPMODE_SRP = 1,
GHWCFG2_OPMODE_NON_HNP_NON_SRP = 2,
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index e4ab16d9d..b92448685 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -32,19 +32,19 @@
#error DWC2 require either CFG_TUH_DWC2_SLAVE_ENABLE or CFG_TUH_DWC2_DMA_ENABLE to be enabled
#endif
-// Debug level for DWC2
-#define DWC2_DEBUG 2
-
#include "host/hcd.h"
#include "host/usbh.h"
#include "dwc2_common.h"
-// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
-#ifndef CFG_TUH_DWC2_ENDPOINT_MAX
-#define CFG_TUH_DWC2_ENDPOINT_MAX 16
-#endif
+ // Debug level for DWC2
+ #define DWC2_DEBUG 2
-#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count
+ // Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
+ #ifndef CFG_TUH_DWC2_ENDPOINT_MAX
+ #define CFG_TUH_DWC2_ENDPOINT_MAX 16u
+ #endif
+
+ #define DWC2_CHANNEL_COUNT_MAX 16u // absolute max channel count
TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
enum {
@@ -79,7 +79,8 @@ typedef struct {
uint32_t speed : 2;
uint32_t next_pid : 2; // PID for next transfer
uint32_t next_do_ping : 1; // Do PING for next transfer if possible (highspeed OUT)
- // uint32_t : 9;
+ uint32_t closing : 1; // endpoint is closing
+ // uint32_t : 8;
};
uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit
@@ -96,6 +97,7 @@ typedef struct {
uint8_t err_count : 3;
uint8_t period_split_nyet_count : 3;
uint8_t halted_nyet : 1;
+ uint8_t closing : 1; // closing channel
};
uint8_t result;
@@ -195,8 +197,21 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint
}
TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
- // disable also require request queue
- TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
+ const bool is_period = channel_is_periodic(channel->hcchar);
+ if (dma_host_enabled(dwc2)) {
+ // In buffer DMA or external DMA mode:
+ // - Channel disable must not be programmed for non-split periodic channels. At the end of the next uframe/frame (in
+ // the worst case), the controller generates a channel halted and disables the channel automatically.
+ // - For split enabled channels (both non-periodic and periodic), channel disable must not be programmed randomly.
+ // However, channel disable can be programmed for specific scenarios such as NAK and FrmOvrn.
+ if (is_period && (channel->hcsplt & HCSPLT_SPLITEN)) {
+ return true;
+ }
+ } else {
+ while (0 == req_queue_avail(dwc2, is_period)) {
+ // blocking wait for request queue available
+ }
+ }
channel->hcintmsk |= HCINT_HALTED;
channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
return true;
@@ -204,7 +219,9 @@ TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2
// attempt to send IN token to receive data
TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
- TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
+ while (0 == req_queue_avail(dwc2, channel_is_periodic(channel->hcchar))) {
+ // blocking wait for request queue available
+ }
channel->hcchar |= HCCHAR_CHENA;
return true;
}
@@ -237,13 +254,37 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) {
return TUSB_INDEX_INVALID_8;
}
-// Find a endpoint that is opened previously with hcd_edpt_open()
+TU_ATTR_ALWAYS_INLINE static inline void edpt_dealloc(hcd_endpoint_t *edpt) {
+ edpt->hcchar_bm.enable = 0;
+}
+
+// close an opened endpoint
+static void edpt_close(dwc2_regs_t *dwc2, uint8_t ep_id) {
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ edpt->closing = 1; // mark endpoint as closing
+
+ // disable active channel belong to this endpoint
+ for (uint8_t ch_id = 0; ch_id < DWC2_CHANNEL_COUNT_MAX; ch_id++) {
+ hcd_xfer_t *xfer = &_hcd_data.xfer[ch_id];
+ if (xfer->allocated && xfer->ep_id == ep_id) {
+ dwc2_channel_t *channel = &dwc2->channel[ch_id];
+ xfer->closing = 1;
+ channel_disable(dwc2, channel);
+ return; // only 1 active channel per endpoint
+ }
+ }
+
+ edpt_dealloc(edpt); // no active channel, safe to de-alloc now
+}
+
+// Find an endpoint that is opened previously with hcd_edpt_open()
// Note: EP0 is bidirectional
TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
- const dwc2_channel_char_t* hcchar_bm = &_hcd_data.edpt[i].hcchar_bm;
- if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr &&
- hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) {
+ const hcd_endpoint_t *edpt = &_hcd_data.edpt[i];
+ const dwc2_channel_char_t hcchar_bm = edpt->hcchar_bm;
+ if (hcchar_bm.enable && hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num &&
+ (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) {
return i;
}
}
@@ -259,8 +300,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t cal_packet_count(uint16_t len, uint
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) {
- if (packet_count & 0x01) {
- return pid ^ 0x02; // toggle DATA0 and DATA1
+ if (packet_count & 0x01u) {
+ return pid ^ 0x02u; // toggle DATA0 and DATA1
} else {
return pid;
}
@@ -456,11 +497,11 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
// HCD closes all opened endpoints belong to this device
void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
- (void) rhport;
- for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
- hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ for (uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
+ const hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
if (edpt->hcchar_bm.enable && edpt->hcchar_bm.dev_addr == dev_addr) {
- tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ edpt_close(dwc2, ep_id);
}
}
}
@@ -503,25 +544,39 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
edpt->speed = bus_info.speed;
edpt->next_pid = HCTSIZ_PID_DATA0;
- if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
- if (bus_info.speed == TUSB_SPEED_FULL) {
- edpt->uframe_interval <<= 3;
- }
- } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
- if (bus_info.speed == TUSB_SPEED_HIGH) {
+ switch (desc_ep->bmAttributes.xfer) {
+ case TUSB_XFER_ISOCHRONOUS:
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
- } else {
- edpt->uframe_interval = desc_ep->bInterval << 3;
- }
+ if (bus_info.speed == TUSB_SPEED_FULL) {
+ edpt->uframe_interval <<= 3;
+ }
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ if (bus_info.speed == TUSB_SPEED_HIGH) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ } else {
+ edpt->uframe_interval = desc_ep->bInterval << 3;
+ }
+ break;
+
+ default:
+ break;
}
return true;
}
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
- (void) rhport; (void) daddr; (void) ep_addr;
- return false; // TODO not implemented yet
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(daddr, ep_num, ep_dir);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+
+ edpt_close(dwc2, ep_id);
+
+ return true;
}
// clean up channel after part of transfer is done but the whole urb is not complete
@@ -590,8 +645,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts
if (dma_host_enabled(dwc2)) {
- uint32_t hcintmsk = HCINT_HALTED;
- channel->hcintmsk = hcintmsk;
+ channel->hcintmsk = HCINT_HALTED;
dwc2->haintmsk |= TU_BIT(ch_id);
channel->hcdma = (uint32_t) edpt->buffer;
@@ -646,7 +700,6 @@ static bool edpt_xfer_kickoff(dwc2_regs_t* dwc2, uint8_t ep_id) {
return channel_xfer_start(dwc2, ch_id);
}
-// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint8_t ep_num = tu_edpt_number(ep_addr);
@@ -654,7 +707,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
- hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ TU_VERIFY(edpt->closing == 0); // skip if endpoint is closing
edpt->buffer = buffer;
edpt->buflen = buflen;
@@ -754,7 +808,7 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
edpt->next_pid = hctsiz.pid; // save PID
edpt->uframe_countdown = edpt->uframe_interval - ucount;
// enable SOF interrupt if not already enabled
- if (!(dwc2->gintmsk & GINTMSK_SOFM)) {
+ if (0 == (dwc2->gintmsk & GINTMSK_SOFM)) {
dwc2->gintsts = GINTSTS_SOF;
dwc2->gintmsk |= GINTMSK_SOFM;
}
@@ -801,7 +855,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
- if (byte_count) {
+ if (byte_count > 0) {
dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count);
xfer->xferred_bytes += byte_count;
xfer->fifo_bytes = byte_count;
@@ -837,8 +891,8 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
dwc2_channel_t* channel = &dwc2->channel[ch_id];
const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
// skip writing to FIFO if channel is expecting halted.
- if (!(channel->hcintmsk & HCINT_HALTED) && (hcchar.ep_dir == TUSB_DIR_OUT)) {
- hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ if (0 == (channel->hcintmsk & HCINT_HALTED) && (hcchar.ep_dir == TUSB_DIR_OUT)) {
+ hcd_xfer_t *xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
@@ -899,6 +953,8 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
} else if (hcint & HCINT_XACT_ERR) {
xfer->err_count++;
channel->hcintmsk |= HCINT_ACK;
+ } else {
+ // nothing to do
}
channel_disable(dwc2, channel);
@@ -910,7 +966,7 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NAK) {
// NAK received, disable channel to flush all posted request and try again
- if (hcsplt.split_en) {
+ if (hcsplt.split_en == 1u) {
hcsplt.split_compl = 0; // restart with start-split
channel->hcsplt = hcsplt.value;
}
@@ -919,8 +975,8 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
- if (hcsplt.split_en) {
- if (!hcsplt.split_compl) {
+ if (hcsplt.split_en == 1u) {
+ if (hcsplt.split_compl == 0) {
// start split is ACK --> do complete split
channel->hcintmsk |= HCINT_NYET;
hcsplt.split_compl = 1;
@@ -932,7 +988,7 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
} else {
// ACK with data
const uint16_t remain_packets = hctsiz.packet_count;
- if (remain_packets) {
+ if (remain_packets > 0) {
// still more packet to receive, also reset to start split
hcsplt.split_compl = 0;
channel->hcsplt = hcsplt.value;
@@ -945,6 +1001,8 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
is_done = true;
} else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else if (xfer->closing == 1) {
is_done = true;
} else {
// got here due to NAK or NYET
@@ -953,6 +1011,8 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
} else if (hcint & HCINT_DATATOGGLE_ERR) {
xfer->err_count = 0;
TU_ASSERT(false);
+ } else {
+ // nothing to do
}
return is_done;
}
@@ -977,7 +1037,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
xfer->err_count = 0;
- if (hcsplt.split_en) {
+ if (hcsplt.split_en == 1u) {
// retry complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
@@ -1005,6 +1065,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
is_done = true;
} else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else if (xfer->closing == 1) {
is_done = true;
} else {
// Got here due to NAK or NYET
@@ -1013,8 +1075,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
- if (hcsplt.split_en) {
- if (!hcsplt.split_compl) {
+ if (hcsplt.split_en == 1u) {
+ if (hcsplt.split_compl == 0) {
// ACK for start split --> do complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
@@ -1026,6 +1088,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
channel->hctsiz &= ~HCTSIZ_DOPING; // HC already cleared PING bit, but we clear anyway
channel->hcchar |= HCCHAR_CHENA;
}
+ } else {
+ // nothing to do
}
if (is_done) {
@@ -1122,6 +1186,10 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
// retry start-split in next binterval
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
+
+ if (xfer->closing == 1) {
+ is_done = true;
+ }
}
return is_done;
@@ -1182,6 +1250,10 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
channel->hcchar |= HCCHAR_CHENA;
}
}
+
+ if (xfer->closing == 1) {
+ is_done = true;
+ }
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
@@ -1226,12 +1298,17 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
} else {
is_done = handle_channel_in_slave(dwc2, ch_id, hcint);
}
- #endif
+ #endif
}
if (is_done) {
- const uint8_t ep_addr = tu_edpt_addr(hcchar.ep_num, hcchar.ep_dir);
- hcd_event_xfer_complete(hcchar.dev_addr, ep_addr, xfer->xferred_bytes, (xfer_result_t)xfer->result, in_isr);
+ if (xfer->closing == 1) {
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[xfer->ep_id];
+ edpt_dealloc(edpt);
+ } else {
+ const uint8_t ep_addr = tu_edpt_addr(hcchar.ep_num, hcchar.ep_dir);
+ hcd_event_xfer_complete(hcchar.dev_addr, ep_addr, xfer->xferred_bytes, (xfer_result_t)xfer->result, in_isr);
+ }
channel_dealloc(dwc2, ch_id);
}
}
@@ -1250,16 +1327,18 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
- hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
- if (edpt->hcchar_bm.enable && channel_is_periodic(edpt->hcchar) && edpt->uframe_countdown > 0) {
- edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
- if (edpt->uframe_countdown == 0) {
- if (!edpt_xfer_kickoff(dwc2, ep_id)) {
- edpt->uframe_countdown = ucount; // failed to start, try again next frame
+ hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id];
+ if (edpt->closing == 0) {
+ if (edpt->hcchar_bm.enable && channel_is_periodic(edpt->hcchar) && edpt->uframe_countdown > 0) {
+ edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
+ if (edpt->uframe_countdown == 0) {
+ if (!edpt_xfer_kickoff(dwc2, ep_id)) {
+ edpt->uframe_countdown = ucount; // failed to start, try again next frame
+ }
}
- }
- more_isr = true;
+ more_isr = true;
+ }
}
}
@@ -1271,9 +1350,9 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
const dwc2_gusbcfg_t gusbcfg = {.value = dwc2->gusbcfg};
- uint32_t phy_clock;
+ uint32_t phy_clock;
- if (gusbcfg.phy_sel) {
+ if (gusbcfg.phy_sel == GUSBCFG_PHYSEL_FULLSPEED) {
phy_clock = 48; // dedicated FS is 48Mhz
if (speed == TUSB_SPEED_LOW) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
@@ -1281,7 +1360,7 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
}
} else {
- if (gusbcfg.ulpi_utmi_sel) {
+ if (gusbcfg.ulpi_utmi_sel == GUSBCFG_PHYHS_ULPI) {
phy_clock = 60; // ULPI 8-bit is 60Mhz
} else {
// UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
@@ -1320,20 +1399,20 @@ static void handle_hprt_irq(uint8_t rhport, bool in_isr) {
const dwc2_hprt_t hprt_bm = {.value = dwc2->hprt};
uint32_t hprt = hprt_bm.value & ~HPRT_W1_MASK;
- if (hprt_bm.conn_detected) {
+ if (hprt_bm.conn_detected == 1u) {
// Port Connect Detect
hprt |= HPRT_CONN_DETECT;
- if (hprt_bm.conn_status) {
+ if (hprt_bm.conn_status == 1u) {
hcd_event_device_attach(rhport, in_isr);
}
}
- if (hprt_bm.enable_change) {
+ if (hprt_bm.enable_change == 1u) {
// Port enable change
hprt |= HPRT_ENABLE_CHANGE;
- if (hprt_bm.enable) {
+ if (hprt_bm.enable == 1u) {
// Port enable
const tusb_speed_t speed = hprt_speed_get(dwc2);
port0_enable(dwc2, speed);
@@ -1392,7 +1471,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
// Device disconnected
dwc2->gintsts = GINTSTS_DISCINT;
- if (!(dwc2->hprt & HPRT_CONN_STATUS)) {
+ if (0 == (dwc2->hprt & HPRT_CONN_STATUS)) {
hcd_event_device_remove(rhport, in_isr);
}
}