fix slave in nak & ack order

This commit is contained in:
hathach 2024-11-02 00:20:40 +07:00
parent c96e04159e
commit a0f75e5b67
No known key found for this signature in database
GPG Key ID: 26FAB84F615C3C52

View File

@ -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 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) // 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; } hcd_xfer_t;
typedef struct { 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 // 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]; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
edpt->next_pid = channel->hctsiz_bm.pid; // save PID 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 /* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB)
* of packets that have been transferred via the USB. This is always an integral number of packets if the transfer * For IN: we can use hctsiz.xfersize as remaining bytes.
* was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the
* bytes transferred via the AHB, not the USB). */ * number of packets that have been transferred via the USB. This is always an integral number of packets if the
const uint16_t remain_packets = channel->hctsiz_bm.packet_count; * transfer was halted before its normal completion.
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; 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->xferred_bytes += actual_bytes;
xfer->out_fifo_bytes = 0;
edpt->buffer += actual_bytes; edpt->buffer += actual_bytes;
edpt->buflen -= 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); bool const is_period = edpt_is_periodic(hcchar_bm->ep_type);
// clear previous state // clear previous state
xfer->out_fifo_bytes = 0; xfer->txfifo_bytes = 0;
// hchar: restore but don't enable yet // hchar: restore but don't enable yet
if (is_period) { 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,); TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); if (byte_count) {
xfer->xferred_bytes += 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;
const uint16_t remain_packets = channel->hctsiz_bm.packet_count; if (byte_count < edpt->hcchar_bm.ep_size) {
if (byte_count < channel->hcchar_bm.ep_size) { // short packet: update PID based on remain packets count
// short packet, minus remaining bytes edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets);
xfer->xferred_bytes -= remain_bytes; } if (remain_packets) {
// update PID based on remain packets count
edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets);
} else {
if (remain_packets) {
channel_send_in_token(dwc2, channel); // still more packet to send 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; const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
for (uint16_t i = 0; i < remain_packets; i++) { 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); 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. // 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; return true;
} }
dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->out_fifo_bytes, xact_bytes); dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->txfifo_bytes, xact_bytes);
xfer->out_fifo_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_disable(dwc2, channel);
channel->hcintmsk &= ~HCINT_ACK; channel->hcintmsk &= ~HCINT_ACK;
} else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) {
channel_disable(dwc2, channel); if (hcint & HCINT_STALL) {
if (hcint & HCINT_XACT_ERR) { 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++; xfer->err_count++;
channel->hcintmsk |= HCINT_ACK; channel->hcintmsk |= HCINT_ACK;
} }
channel_disable(dwc2, channel);
} else if (hcint & HCINT_HALTED) { } else if (hcint & HCINT_HALTED) {
channel->hcintmsk &= ~HCINT_HALTED; channel->hcintmsk &= ~HCINT_HALTED;
if (xfer->result != XFER_RESULT_INVALID) { 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; is_done = true;
} else { } else {
// Re-initialize Channel // 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) { } else if (hcint & HCINT_DATATOGGLE_ERR) {
xfer->err_count = 0; xfer->err_count = 0;
TU_ASSERT(false);
} else if (hcint & HCINT_NAK) { } else if (hcint & HCINT_NAK) {
// NAK received, re-enable channel if request queue is available // NAK received, re-enable channel if request queue is available
channel_send_in_token(dwc2, channel); channel_send_in_token(dwc2, channel);
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
} }
return is_done; 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); channel_disable(dwc2, channel);
} else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) {
// clean up transfer so far, disable and start again later // 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); channel_disable(dwc2, channel);
if (hcint & HCINT_XACT_ERR) { if (hcint & HCINT_XACT_ERR) {
xfer->err_count++; 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) { if (is_done) {
xfer->xferred_bytes += xfer->out_fifo_bytes; xfer->xferred_bytes += xfer->txfifo_bytes;
xfer->out_fifo_bytes = 0; xfer->txfifo_bytes = 0;
} }
return is_done; 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; xfer->result = XFER_RESULT_FAILED;
} else { } else {
channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
// re-init channel edpt->next_pid = channel->hctsiz_bm.pid; // save PID
TU_ASSERT(false); 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)) { } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
xfer->err_count = 0; 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; xfer->xferred_bytes += edpt->buflen;
} else { } else {
xfer->result = XFER_RESULT_STALLED; xfer->result = XFER_RESULT_STALLED;
channel_xfer_cleanup(dwc2, ch_id); channel_xfer_wrapup(dwc2, ch_id);
} }
channel->hcintmsk &= ~HCINT_ACK; channel->hcintmsk &= ~HCINT_ACK;
} else if (hcint & HCINT_XACT_ERR) { } else if (hcint & HCINT_XACT_ERR) {
if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) {
xfer->err_count = 0; xfer->err_count = 0;
// clean up transfer so far and start again // 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); channel_xfer_start(dwc2, ch_id);
} else { } else {
xfer->err_count++; 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; is_done = true;
} else { } else {
// clean up transfer so far and start again // 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); 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; dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
uint32_t hcint = channel->hcint; uint32_t hcint = channel->hcint;
channel->hcint = (hcint & channel->hcintmsk); // clear enabled interrupts channel->hcint = hcint;
bool is_done; bool is_done;
if (is_dma) { if (is_dma) {
@ -1117,6 +1134,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
handle_hprt_irq(rhport, 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 #if CFG_TUH_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling, must be handled before HCINT // RxFIFO non-empty interrupt handling, must be handled before HCINT
if (gintsts & GINTSTS_RXFLVL) { if (gintsts & GINTSTS_RXFLVL) {
@ -1139,12 +1162,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
} }
} }
#endif #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 #endif