fix slave in nak & ack order
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 hathach
					hathach