summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-04 15:31:45 +0700
committerhathach <[email protected]>2026-06-04 15:31:45 +0700
commit0393a5eaa1fc9ef33cbfd847c924cb83d82696f3 (patch)
treeb96374c05142757820304e2d89d6f260b24f1354 /src
parent232315d6909d9ca7948c830a5e43ad35189da57f (diff)
parentac32feafeb2fb0f6cc5c3010d32db319ff15ca64 (diff)
Merge branch 'master' into dwc2_postfix
Diffstat (limited to 'src')
-rw-r--r--src/class/mtp/mtp_device.c11
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c60
2 files changed, 42 insertions, 29 deletions
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
index fd06b4601..1f76dfcc7 100644
--- a/src/class/mtp/mtp_device.c
+++ b/src/class/mtp/mtp_device.c
@@ -443,15 +443,12 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Check completion for IN and OUT separately
bool is_complete;
-
- if (is_data_in)
- {
+ if (is_data_in) {
// IN completion: short packet, ZLP, or reaching total_len
is_complete = (xferred_bytes == 0 || xferred_bytes < threshold || p_mtp->xferred_len >= p_mtp->total_len);
- }
- else
- {
- // OUT completion: reaching total_len or ZLP
+ } else {
+ // OUT completion: reaching total_len or ZLP only. A short packet does NOT end the phase
+ // (an early short packet before total_len is the cancel case, not normal completion).
is_complete = (p_mtp->xferred_len >= p_mtp->total_len) || ((xferred_bytes == 0 && p_mtp->xferred_len > 0));
}
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index e6d7dc08e..c7899a354 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -793,15 +793,15 @@ static void handle_bus_reset(uint8_t rhport) {
xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
- uint32_t oepmsk = 0;
+ uint32_t gintmsk = GINTMSK_OTGINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
if(dma_device_enabled(dwc2)) {
- oepmsk = GINTMSK_OEPINT;
+ gintmsk |= GINTMSK_OEPINT;
dma_setup_prepare(rhport);
} else {
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
- dwc2->gintmsk |= GINTMSK_OTGINT | oepmsk | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
+ dwc2->gintmsk |= gintmsk;
}
static void handle_enum_done(uint8_t rhport) {
@@ -885,45 +885,47 @@ static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+ // DWC2 v3.10a (e.g. STM32L476) emits an extra EP0 RX_COMPLETE that is NOT a real OUT data transfer completion, in two
+ // situations - each flagged by a DOEPINT bit set on that word:
+ // - DOEPINT.STPKTRX (Setup Packet Received): pushed between SETUP_RX and SETUP_DONE of every control transfer.
+ // - DOEPINT.STSPHSRX (Status Phase Received for control write): pushed after the OUT data stage when the host
+ // starts the IN status phase.
+ // Both are dropped in the RX_COMPLETE case below, clearing the flag (W1C) so a latched STSPHSRX
+ // does not block the core from emitting the next SETUP_DONE. usbd still processes the real OUT data
+ // and queues the IN status ZLP itself - the core does not auto-complete the control-write status.
+ const bool quirk_v310a = (dwc2->gsnpsid == DWC2_CORE_REV_3_10a);
+
// Pop control word off FIFO
const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp};
+ const uint8_t packet_status = grxstsp.packet_status;
const uint8_t epnum = grxstsp.ep_ch_num;
dwc2_dep_t* epout = &dwc2->epout[epnum];
- switch (grxstsp.packet_status) {
+ switch (packet_status) {
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
// Global OUT NAK: do nothing
break;
case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
- uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_buffer;
+ uint32_t * setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_buffer;
// We can receive up to three setup packets in succession, but only the last one is valid.
setup[0] = (*rx_fifo);
setup[1] = (*rx_fifo);
-
- dwc2_dep_t* epin0 = &dwc2->epin[0];
- if (edpt_is_enabled(epin0)) {
- edpt_disable(rhport, 0x80, false);
- }
-
- // (GenID < 3.00a) Must wait SETUP_DONE before next OUT transfer, otherwise OUT data may be corrupted.
- // (GenID >= 3.00a) On the other hand STUPCNT is auto reloaded and SETUP_DONE is only triggered once after bus reset.
- if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) {
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
- }
break;
}
- case GRXSTS_PKTSTS_SETUP_DONE:
- // Setup packet done:
+ case GRXSTS_PKTSTS_SETUP_DONE: {
+ // Pop this word causes the Setup interrupt
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
-
- if (dwc2->gsnpsid < DWC2_CORE_REV_3_00a) {
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
+ epout->doepint = DOEPINT_SETUP | DOEPINT_STPKTRX; // Clear SETUP interrupt, required for core to re-write this control word
+ if (edpt_is_enabled(&dwc2->epin[0])) {
+ edpt_disable(rhport, 0x80, false);
}
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
break;
+ }
case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
@@ -952,7 +954,19 @@ static void handle_rxflvl_irq(uint8_t rhport) {
}
case GRXSTS_PKTSTS_RX_COMPLETE: {
- // Out packet done
+ // Pop this word causes the xfer complete interrupt
+ const uint32_t doepint = epout->doepint;
+ epout->doepint = DOEPINT_XFRC;
+
+ // v3.10a quirk (see top of function): the extra RX_COMPLETE flagged with Setup Packet Received (STPKTRX) or
+ // Status Phase Received for control write (STSPHSRX) is not a real OUT completion. Drop it
+ if (quirk_v310a) {
+ if (doepint & (DOEPINT_STPKTRX | DOEPINT_STSPHSRX)) {
+ epout->doepint = DOEPINT_STPKTRX | DOEPINT_STSPHSRX;
+ break;
+ }
+ }
+
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
if (epnum == 0 && _dcd_data.ep0_pending[TUSB_DIR_OUT] > 0) {
// EP0 can only handle one packet, schedule another packet to be received.
@@ -1096,6 +1110,8 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
#if CFG_TUD_DWC2_SLAVE_ENABLE
if (dir == TUSB_DIR_IN) {
handle_epin_slave(rhport, epnum, intr.diepint_bm);
+ } else {
+ // epout is handled in handle_rxflvl_irq
}
#endif
}