From a0f75e5b6742c5365bbcea079dab75242230fe60 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 2 Nov 2024 00:20:40 +0700 Subject: [PATCH] fix slave in nak & ack order --- src/portable/synopsys/dwc2/hcd_dwc2.c | 109 +++++++++++++++----------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index b6509ad4e..42a5b9fbd 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -87,7 +87,7 @@ typedef struct { uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_xfer_start() (retry with NAK/NYET) - uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). + uint16_t txfifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). } hcd_xfer_t; typedef struct { @@ -489,22 +489,29 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* } // clean up channel after part of transfer is done but the whole urb is not complete -static void channel_xfer_cleanup(dwc2_regs_t* dwc2, uint8_t ch_id) { +static void channel_xfer_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; edpt->next_pid = channel->hctsiz_bm.pid; // save PID - /* Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the number - * of packets that have been transferred via the USB. This is always an integral number of packets if the transfer - * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of - * bytes transferred via the AHB, not the USB). */ - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); - const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + /* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB) + * For IN: we can use hctsiz.xfersize as remaining bytes. + * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the + * number of packets that have been transferred via the USB. This is always an integral number of packets if the + * transfer was halted before its normal completion. + */ + uint16_t actual_bytes; + if (channel->hcchar_bm.ep_dir == TUSB_DIR_IN) { + actual_bytes = edpt->buflen - channel->hctsiz_bm.xfer_size; + } else { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); + actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + } + xfer->txfifo_bytes = 0; xfer->xferred_bytes += actual_bytes; - xfer->out_fifo_bytes = 0; edpt->buffer += actual_bytes; edpt->buflen -= actual_bytes; } @@ -517,7 +524,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // clear previous state - xfer->out_fifo_bytes = 0; + xfer->txfifo_bytes = 0; // hchar: restore but don't enable yet if (is_period) { @@ -691,19 +698,15 @@ 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]; - dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); - xfer->xferred_bytes += byte_count; + if (byte_count) { + dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); + xfer->xferred_bytes += byte_count; - const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - if (byte_count < channel->hcchar_bm.ep_size) { - // short packet, minus remaining bytes - xfer->xferred_bytes -= remain_bytes; - - // update PID based on remain packets count - edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); - } else { - if (remain_packets) { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + if (byte_count < edpt->hcchar_bm.ep_size) { + // short packet: update PID based on remain packets count + edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); + } if (remain_packets) { channel_send_in_token(dwc2, channel); // still more packet to send } } @@ -744,7 +747,7 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = edpt->buflen - xfer->out_fifo_bytes; + const uint16_t remain_bytes = edpt->buflen - xfer->txfifo_bytes; const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); // skip if there is not enough space in FIFO and RequestQueue. @@ -753,8 +756,8 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { return true; } - dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->out_fifo_bytes, xact_bytes); - xfer->out_fifo_bytes += xact_bytes; + dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->txfifo_bytes, xact_bytes); + xfer->txfifo_bytes += xact_bytes; } } } @@ -772,11 +775,15 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h channel_disable(dwc2, channel); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { - channel_disable(dwc2, channel); - if (hcint & HCINT_XACT_ERR) { + if (hcint & HCINT_STALL) { + xfer->result = XFER_RESULT_STALLED; + } else if (hcint & HCINT_BABBLE_ERR) { + xfer->result = XFER_RESULT_FAILED; + } else if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; } + channel_disable(dwc2, channel); } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; if (xfer->result != XFER_RESULT_INVALID) { @@ -786,15 +793,17 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h is_done = true; } else { // Re-initialize Channel + TU_ASSERT(false); } - } else if (hcint & HCINT_ACK) { - xfer->err_count = 0; - channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_DATATOGGLE_ERR) { xfer->err_count = 0; + TU_ASSERT(false); } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available channel_send_in_token(dwc2, channel); + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; } return is_done; @@ -814,7 +823,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_NAK | HCINT_XACT_ERR | HCINT_NYET)) { // clean up transfer so far, disable and start again later - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_disable(dwc2, channel); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; @@ -841,8 +850,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t } if (is_done) { - xfer->xferred_bytes += xfer->out_fifo_bytes; - xfer->out_fifo_bytes = 0; + xfer->xferred_bytes += xfer->txfifo_bytes; + xfer->txfifo_bytes = 0; } return is_done; @@ -878,8 +887,16 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci xfer->result = XFER_RESULT_FAILED; } else { channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; - // re-init channel - TU_ASSERT(false); + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ + // for periodic, de-allocate channel, enable SOF set frame counter for later transfer + channel_dealloc(dwc2, ch_id); + edpt->frame_countdown = edpt->frame_interval; + dwc2->gintmsk |= GINTSTS_SOF; + } else { + // for control/bulk: try again immediately + TU_ASSERT(false); + } } } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { xfer->err_count = 0; @@ -918,14 +935,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc xfer->xferred_bytes += edpt->buflen; } else { xfer->result = XFER_RESULT_STALLED; - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); } channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { xfer->err_count = 0; // clean up transfer so far and start again - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } else { xfer->err_count++; @@ -934,7 +951,7 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc is_done = true; } else { // clean up transfer so far and start again - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } } @@ -961,7 +978,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; uint32_t hcint = channel->hcint; - channel->hcint = (hcint & channel->hcintmsk); // clear enabled interrupts + channel->hcint = hcint; bool is_done; if (is_dma) { @@ -1117,6 +1134,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_hprt_irq(rhport, in_isr); } + if (gintsts & GINTSTS_HCINT) { + // Host Channel interrupt: source is cleared in HCINT register + // must be handled after TX FIFO empty + handle_channel_irq(rhport, in_isr); + } + #if CFG_TUH_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling, must be handled before HCINT if (gintsts & GINTSTS_RXFLVL) { @@ -1139,12 +1162,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } } #endif - - if (gintsts & GINTSTS_HCINT) { - // Host Channel interrupt: source is cleared in HCINT register - // must bee handled after TX FIFO empty - handle_channel_irq(rhport, in_isr); - } } #endif