diff options
| author | hathach <[email protected]> | 2026-06-04 15:31:45 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-06-04 15:31:45 +0700 |
| commit | 0393a5eaa1fc9ef33cbfd847c924cb83d82696f3 (patch) | |
| tree | b96374c05142757820304e2d89d6f260b24f1354 /src | |
| parent | 232315d6909d9ca7948c830a5e43ad35189da57f (diff) | |
| parent | ac32feafeb2fb0f6cc5c3010d32db319ff15ca64 (diff) | |
Merge branch 'master' into dwc2_postfix
Diffstat (limited to 'src')
| -rw-r--r-- | src/class/mtp/mtp_device.c | 11 | ||||
| -rw-r--r-- | src/portable/synopsys/dwc2/dcd_dwc2.c | 60 |
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 } |
