mirror of
https://github.com/hathach/tinyusb.git
synced 2025-01-31 05:52:55 +08:00
fix slave in nak & ack order
This commit is contained in:
parent
c96e04159e
commit
a0f75e5b67
@ -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). */
|
||||
/* 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);
|
||||
const uint16_t actual_bytes = (total_packets - remain_packets) * 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];
|
||||
|
||||
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
|
||||
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);
|
||||
} else {
|
||||
if (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,9 +887,17 @@ 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
|
||||
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;
|
||||
channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR);
|
||||
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user