-
Notifications
You must be signed in to change notification settings - Fork 1.3k
dcd/dwc2: fix EP0 multi-packet transfer logic #3295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…m_packets = 1 Signed-off-by: HiFiPhile <[email protected]>
Signed-off-by: HiFiPhile <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes EP0 multi-packet transfer logic in the DWC2 driver and adds configurable EP0 buffer size support. The issue was in the edpt_schedule_packets function where ep0_pending was incorrectly being set to xfer->max_size instead of the proper endpoint size, causing transfer size calculation errors.
Key changes:
- Added
CFG_TUD_EP0_BUFSIZEconfiguration option for EP0 buffer size - Fixed EP0 packet scheduling logic to use correct endpoint size
- Enhanced packet transmission handling for single vs multi-packet transfers
- Updated DFU examples with BOS descriptors and Microsoft OS 2.0 compatibility
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tusb_option.h | Added CFG_TUD_EP0_BUFSIZE configuration option |
| src/portable/synopsys/dwc2/dcd_dwc2.c | Fixed EP0 transfer logic and improved packet handling |
| src/device/usbd_control.c | Updated to use new CFG_TUD_EP0_BUFSIZE configuration |
| examples/device/dfu_runtime/src/usb_descriptors.c | Added BOS descriptor and Microsoft OS 2.0 support |
| examples/device/dfu/src/usb_descriptors.c | Added BOS descriptor and Microsoft OS 2.0 support |
Signed-off-by: HiFiPhile <[email protected]>
Signed-off-by: HiFiPhile <[email protected]>
…to/from use EP0 buffer. Signed-off-by: HiFiPhile <[email protected]>
# Conflicts: # examples/device/dfu/src/usb_descriptors.c # examples/device/dfu_runtime/src/usb_descriptors.c # src/portable/synopsys/dwc2/dcd_dwc2.c
| // a short packet is sent including zero-length packet. | ||
| if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || | ||
| (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) { | ||
| (xferred_bytes < CFG_TUD_EP0_BUFSIZE)) { |
Check warning
Code scanning / C-STAT
The operands xferred_bytes' and CFG_TUD_ENDPOINT0_SIZE' have essential type categories unsigned 32-bit int and signed 8-bit int, which do not match Warning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot most of the dwc2 driver, didn't re-check the specs. These are 2 things pop-up in my mind when we are making control transfer faster like bulk.
PS: also I enable a few code scanning tools (free for OSS), still testing it out, so it can be very verbose atm
| // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable | ||
| if (dir == TUSB_DIR_IN && total_bytes != 0) { | ||
| dwc2->diepempmsk |= (1u << epnum); | ||
| // For num_packets = 1 we write the packet directly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep using the TXFE interrupt to write to fifo. Otherwise there could be race condition whereas usbd mis-manage control transfer, and we are attempting to xfer on busy endpoint0.
also I think we need to also increase (double) the dfifo_alloc() for EP0 when the EP0_BUFSIZE is large enough e.g 8x64 bytes since user is clearly want to make a large transfer with EP0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep using the TXFE interrupt to write to fifo. Otherwise there could be race condition whereas usbd mis-manage control transfer, and we are attempting to xfer on busy endpoint0.
I think it should be safe, which scenario you are thinking about ?
also I think we need to also increase (double) the dfifo_alloc() for EP0 when the EP0_BUFSIZE is large enough e.g 8x64 bytes since user is clearly want to make a large transfer with EP0
EP0 IN can only queue 3 packets each time, EP0 OUT can only queue 1 packet each time, so increasing the fifo won't have much help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be safe, which scenario you are thinking about ?
You are right, I just feel a bit un-easy when both handle_epin_slave() and edpt_schedule_packets() can write to difof. Especially handle_epin_slave() also called edpt_schedule_packets() in case of ep0. But it is probably safe enough, actually we can refactor this and try to write as many packet as we could before enable the TX empty if there is remaining packet ?
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
const uint16_t remain_packets = tsiz.packet_count;
// Process every single packet (only whole packets can be written to fifo)
for (uint16_t i = 0; i < remain_packets; i++) {
tsiz.value = epin->tsiz;
const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
// Check if dtxfsts has enough space available
if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
break;
}
// Push packet to Tx-FIFO
if (xfer->ff) {
volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
} else {
dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
xfer->buffer += xact_bytes;
}
}EP0 IN can only queue 3 packets each time, EP0 OUT can only queue 1 packet each time, so increasing the fifo won't have much help.
right, again, I am too lazy to check the specs, the increased ep0 bufsize only help to reduce the overhead when pushing isr --> usbd event stack. Let keep the ep0 1 packet per xfer is good enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've refactored the fifo writing routine and added a tunable CFG_TUD_DWC2_IN_DOUBLE_BUFFER to set GAHBCFG_TX_FIFO_EPMTY_LVL and fifo allocation, in order to allow writing 2 packets to fifo.
By default we allocate fifo size=packet size, the writing loop will likely stop after one packet is written.
| if (epnum == 0) { | ||
| total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size); | ||
| total_bytes = tu_min16(_dcd_data.ep0_pending[dir], CFG_TUD_ENDPOINT0_SIZE); | ||
| _dcd_data.ep0_pending[dir] -= total_bytes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should not pre-calculate the pending, we should just store the total_bytes and minus xferred_bytes in complete isr instead like other bulk transfers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can't get rid of ep0_pending, otherwise we need to add ep0_xferred_bytes which ends up the same thing, just one counting up another counting down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, ep0_xferred_bytes is better since it will only increased when xfer is complete, and we can also use that in case of error for xferred so far. Maybe i will do that in follow-up PR later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For OUT transfer it's easy to get ep0_xferred_bytes, but for IN transfer it can't be read from register, we have to add something like last_xfer_bytes and set it in edpt_schedule_packets. I don't know if it's worth the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, sorry, I forgot some of these thing.
Signed-off-by: Mengsk <[email protected]>
src/tusb_option.h
Outdated
|
|
||
| // Allocate double buffer for IN endpoint for better performance (only applies to non-periodic endpoints) | ||
| #ifndef CFG_TUD_DWC2_IN_DOUBLE_BUFFER | ||
| #define CFG_TUD_DWC2_IN_DOUBLE_BUFFER 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should not introduce this option. I always think the fine-tune fifo should be dictated by application per endpoint basic. E.g MSC + CDC, we would want a massive endpoint size for msc and smaller for cdc. I am think we should do that via a tud_configure() (which allo to pass an option id + pointer to struct) that fine tune the behavior of dcd or stack. similar to tuh_configure() https://github.com/hathach/tinyusb/blob/master/src/host/usbh.h#L145 . dwc2 option may include grxsize and txfifo size for each endpoint (0 for auto as current).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah a configurable parameter is better, didn't notice there is a tuh_configure().
| if (epnum == 0) { | ||
| total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size); | ||
| total_bytes = tu_min16(_dcd_data.ep0_pending[dir], CFG_TUD_ENDPOINT0_SIZE); | ||
| _dcd_data.ep0_pending[dir] -= total_bytes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, sorry, I forgot some of these thing.
| xfer->buffer += xact_bytes; | ||
| } | ||
| } | ||
| epin_write_tx_fifo(rhport, epnum); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have a possibility to call epin_write_tx_fifo() 2nd time with ep0. although it would not write any bytes, I think we should skip this for ep0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right.
Signed-off-by: HiFiPhile <[email protected]>
Signed-off-by: HiFiPhile <[email protected]>
|


Describe the PR
There was an issue in
edpt_schedule_packets, asep0_pending = xfer->max_sizeis set indcd_edpt_xfer, ep0_pending will become 0.The issue didn't broken since we schedule only one control transfer each time.
I've add an option
CFG_TUD_EP0_BUFSIZEto allow setting EP0 buffer size individually, tested with DFU examples withCFG_TUD_EP0_BUFSIZE=512