Skip to content

dwc2 postfixes#3666

Merged
hathach merged 6 commits into
masterfrom
dwc2_postfix
Jun 4, 2026
Merged

dwc2 postfixes#3666
hathach merged 6 commits into
masterfrom
dwc2_postfix

Conversation

@HiFiPhile

Copy link
Copy Markdown
Collaborator

Even if STUPCNT=1 the DMA always receives 3 SETUP packets before address rewind, a buffer of 3 packets is needed.

Follow the manual to use DOEPDMA0 to get the latest packet.

For testing it's not easy with PC, actually I hacked USBH to send 3/4 SETUP packets:
image

Copilot AI review requested due to automatic review settings June 2, 2026 20:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts the DWC2 device controller (DCD) EP0 SETUP handling in DMA mode to cope with back-to-back SETUP packets by allocating room for multiple SETUP packets and selecting the most recent packet via DOEPDMA0, aligning behavior with the DWC2 manual’s guidance.

Changes:

  • Expand the EP0 SETUP DMA buffer from 8 bytes to 24 bytes (3 packets).
  • Program EP0 OUT to receive back-to-back SETUP packets (via STUPCNT).
  • Use DOEPDMA0 to locate and forward the latest SETUP packet to the TinyUSB stack.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/portable/synopsys/dwc2/dcd_dwc2.c
Comment thread src/portable/synopsys/dwc2/dcd_dwc2.c Outdated
Comment thread src/portable/synopsys/dwc2/dcd_dwc2.c
@github-actions

This comment was marked as outdated.

Signed-off-by: HiFiPhile <admin@hifiphile.com>
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

Top 1 targets by memory change (%) (out of 2345 targets) View Project Dashboard →

target .text .rodata .data .bss total % diff
raspberrypi_zero/mtp 61,528 → 61,524 (-4) 68,851 → 68,847 (-4) -0.0%

@hathach

hathach commented Jun 3, 2026

Copy link
Copy Markdown
Owner

@claude why your auto review does not work with this PR, it is not from fork

@claude

claude Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Claude finished @hathach's task in 3m 32s —— View job


Auto-review Investigation

  • Check workflow configuration
  • Check CI run status for this PR
  • Inspect the Claude Code Review job log

The auto-review did actually run on this PR. Here's what I found:

Workflow run: Claude Code Review #26847730488

  • Status: ✅ completed / success
  • Ran: 2026-06-02T21:00:07Z → 21:07:16Z (~7 min)
  • 10 turns, $2.10 cost
  • permission_denials_count: 1

Why you may not see a review comment:

The job completed, but there was 1 permission denial during the Claude session. This likely caused the sticky comment (use_sticky_comment: true) to not be posted, or to be posted empty. The CI runner still exited with success because the job itself didn't crash — the denial was silently swallowed.

The show_full_output: false in the workflow hides Claude's transcript, so I can't see exactly which tool call was denied. The most likely candidates based on the current .claude/settings.json permission config are:

  1. A Bash tool call — the project settings don't allowlist Bash for the review runner, and the code-review plugin may have tried to run a shell command.
  2. A GitHub API call — e.g., trying to fetch CI artifacts or post a formal PR review (vs. a comment).

To diagnose further, you could temporarily add show_full_output: true to claude-code-review.yml and re-trigger by closing/reopening the PR. That would expose the full Claude transcript in the Actions log.

To re-trigger now (without changing the workflow), push a no-op commit to dwc2_postfix or close and reopen the PR — both fire the synchronize/reopened event which the workflow listens to.


@hathach

This comment was marked as outdated.

@claude

This comment was marked as outdated.

@hathach

This comment was marked as off-topic.

@claude

This comment was marked as outdated.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

TinyUSB Average Code Size Metrics

File .text .rodata .data .bss size %
ehci.c 2763 0 0 6274 7783 4.4%
midi2_host.c 1802 0 0 5921 7723 4.3%
cdc_host.c 6381 487 15 985 7579 4.3%
usbh.c 4967 57 82 1161 6233 3.5%
ncm_device.c 1757 28 815 4354 6124 3.4%
hcd_dwc2.c 5015 25 1 513 5554 3.1%
midi_host.c 1341 7 7 3635 4979 2.8%
video_device.c 4443 5 1235 479 4914 2.8%
audio_device.c 2896 0 1259 1625 4515 2.5%
dcd_dwc2.c 4223 19 0 265 4506 2.5%
ohci.c 1925 0 0 2503 4428 2.5%
dcd_ch32_usbfs.c 1582 0 0 2444 4026 2.3%
usbd.c 3519 58 91 355 3936 2.2%
ecm_rndis_device.c 1059 0 1 2759 3818 2.1%
hcd_stm32_fsdev.c 3257 0 1 420 3678 2.1%
dcd_ft9xx.c 3284 0 0 172 3456 1.9%
msc_device.c 2517 108 2281 806 3323 1.9%
dcd_khci.c 1952 0 0 1290 3242 1.8%
dcd_nrf5x.c 2939 0 0 292 3231 1.8%
hcd_musb.c 3071 0 0 157 3228 1.8%
midi2_device.c 2634 52 1175 566 3222 1.8%
dcd_ci_fs.c 1924 0 0 1290 3214 1.8%
dcd_da146xx.c 3067 0 0 144 3211 1.8%
hcd_rusb2.c 2923 0 0 245 3168 1.8%
dcd_rusb2.c 2918 0 0 156 3074 1.7%
hcd_ch32_usbfs.c 2491 0 0 502 2993 1.7%
hcd_khci.c 2443 0 0 454 2897 1.6%
dcd_stm32_fsdev.c 2558 0 0 291 2849 1.6%
dcd_mm32f327x_otg.c 1477 0 0 1290 2767 1.6%
usbtmc_device.c 2196 24 68 316 2544 1.4%
hcd_samd.c 2220 0 0 324 2544 1.4%
dcd_ci_hs.c 1756 0 0 1344 2534 1.4%
dcd_eptri.c 2273 0 0 259 2532 1.4%
hid_host.c 1241 0 0 1251 2492 1.4%
dcd_musb.c 2225 0 0 171 2396 1.3%
dcd_ch32_usbhs.c 1892 0 0 481 2373 1.3%
hcd_rp2040.c 1996 17 4 321 2338 1.3%
mtp_device.c 1717 22 743 588 2312 1.3%
dcd_rp2040.c 840 0 764 653 2257 1.3%
msc_host.c 1636 0 0 394 2030 1.1%
dcd_msp430x5xx.c 1801 0 0 176 1977 1.1%
cdc_device.c 1239 16 1092 735 1972 1.1%
dcd_lpc17_40.c 1481 0 0 648 1805 1.0%
midi_device.c 1151 0 1007 624 1773 1.0%
dcd_nuc505.c 0 0 1533 157 1690 1.0%
dcd_lpc_ip3511.c 1463 0 0 264 1683 0.9%
hub.c 1384 8 8 30 1418 0.8%
printer_device.c 830 0 706 566 1394 0.8%
dcd_samg.c 1322 0 0 72 1394 0.8%
dcd_samd.c 1036 0 0 266 1302 0.7%
dcd_nuc121.c 1170 0 0 101 1270 0.7%
hid_device.c 1125 44 997 119 1244 0.7%
vendor_device.c 641 0 534 565 1204 0.7%
dcd_nuc120.c 1096 0 0 78 1174 0.7%
rp2040_usb.c 386 35 619 11 1051 0.6%
dfu_device.c 777 28 712 138 914 0.5%
tusb_fifo.c 854 0 486 0 849 0.5%
typec_stm32.c 820 8 2 12 842 0.5%
dwc2_common.c 603 22 0 0 615 0.3%
usbc.c 420 2 20 166 608 0.3%
hcd_pio_usb.c 262 0 240 0 502 0.3%
tusb.c 455 0 387 3 457 0.3%
hcd_ci_hs.c 181 0 0 0 181 0.1%
fsdev_common.c 180 0 0 0 180 0.1%
rusb2_common.c 160 0 16 0 176 0.1%
dfu_rt_device.c 157 0 134 0 157 0.1%
TOTAL 124114 1072 17035 52181 177855 100.0%
Input files
  • cmake-build/cmake-build-adafruit_clue/metrics.json
  • cmake-build/cmake-build-apard32690/metrics.json
  • cmake-build/cmake-build-at32f403a_weact_blackpill/metrics.json
  • cmake-build/cmake-build-at_start_f402/metrics.json
  • cmake-build/cmake-build-at_start_f413/metrics.json
  • cmake-build/cmake-build-at_start_f415/metrics.json
  • cmake-build/cmake-build-at_start_f423/metrics.json
  • cmake-build/cmake-build-at_start_f425/metrics.json
  • cmake-build/cmake-build-at_start_f435/metrics.json
  • cmake-build/cmake-build-at_start_f455/metrics.json
  • cmake-build/cmake-build-b_g474e_dpow1/metrics.json
  • cmake-build/cmake-build-b_u585i_iot2a/metrics.json
  • cmake-build/cmake-build-ch32f205r-r0/metrics.json
  • cmake-build/cmake-build-ch32v103c_bluepill/metrics.json
  • cmake-build/cmake-build-ch32v203c_r0_1v0/metrics.json
  • cmake-build/cmake-build-ch32v307v_r1_1v0/metrics.json
  • cmake-build/cmake-build-cynthion_d11/metrics.json
  • cmake-build/cmake-build-da14695_dk_usb/metrics.json
  • cmake-build/cmake-build-double_m33_express/metrics.json
  • cmake-build/cmake-build-ea4088_quickstart/metrics.json
  • cmake-build/cmake-build-ea4357/metrics.json
  • cmake-build/cmake-build-ek_tm4c123gxl/metrics.json
  • cmake-build/cmake-build-feather_stm32f405/metrics.json
  • cmake-build/cmake-build-fomu/metrics.json
  • cmake-build/cmake-build-frdm_k32l2a4s/metrics.json
  • cmake-build/cmake-build-frdm_k64f/metrics.json
  • cmake-build/cmake-build-frdm_kl25z/metrics.json
  • cmake-build/cmake-build-frdm_mcxa153/metrics.json
  • cmake-build/cmake-build-frdm_rw612/metrics.json
  • cmake-build/cmake-build-hpm6750evk2/metrics.json
  • cmake-build/cmake-build-lpcxpresso11u37/metrics.json
  • cmake-build/cmake-build-lpcxpresso1347/metrics.json
  • cmake-build/cmake-build-lpcxpresso1549/metrics.json
  • cmake-build/cmake-build-lpcxpresso1769/metrics.json
  • cmake-build/cmake-build-lpcxpresso18s37/metrics.json
  • cmake-build/cmake-build-lpcxpresso51u68/metrics.json
  • cmake-build/cmake-build-lpcxpresso54114/metrics.json
  • cmake-build/cmake-build-metro_m0_express/metrics.json
  • cmake-build/cmake-build-metro_m4_express/metrics.json
  • cmake-build/cmake-build-metro_m7_1011/metrics.json
  • cmake-build/cmake-build-mm32f327x_mb39/metrics.json
  • cmake-build/cmake-build-mm900evxb/metrics.json
  • cmake-build/cmake-build-msp_exp430f5529lp/metrics.json
  • cmake-build/cmake-build-msp_exp432e401y/metrics.json
  • cmake-build/cmake-build-nutiny_nuc126v/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc120/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc121/metrics.json
  • cmake-build/cmake-build-nutiny_sdk_nuc505/metrics.json
  • cmake-build/cmake-build-portenta_c33/metrics.json
  • cmake-build/cmake-build-raspberry_pi_pico/metrics.json
  • cmake-build/cmake-build-raspberrypi_cm4/metrics.json
  • cmake-build/cmake-build-raspberrypi_zero/metrics.json
  • cmake-build/cmake-build-samg55_xplained/metrics.json
  • cmake-build/cmake-build-sipeed_longan_nano/metrics.json
  • cmake-build/cmake-build-stlinkv3mini/metrics.json
  • cmake-build/cmake-build-stm32c071nucleo/metrics.json
  • cmake-build/cmake-build-stm32f070rbnucleo/metrics.json
  • cmake-build/cmake-build-stm32f103_bluepill/metrics.json
  • cmake-build/cmake-build-stm32f207nucleo/metrics.json
  • cmake-build/cmake-build-stm32f303disco/metrics.json
  • cmake-build/cmake-build-stm32g0b1nucleo/metrics.json
  • cmake-build/cmake-build-stm32h503nucleo/metrics.json
  • cmake-build/cmake-build-stm32h743eval/metrics.json
  • cmake-build/cmake-build-stm32h7s3nucleo/metrics.json
  • cmake-build/cmake-build-stm32l052dap52/metrics.json
  • cmake-build/cmake-build-stm32l412nucleo/metrics.json
  • cmake-build/cmake-build-stm32n6570dk/metrics.json
  • cmake-build/cmake-build-stm32u083cdk/metrics.json
  • cmake-build/cmake-build-stm32wb55nucleo/metrics.json
  • cmake-build/cmake-build-stm32wba_nucleo/metrics.json
  • cmake-build/cmake-build-xmc4500_relax/metrics.json

@hathach

hathach commented Jun 4, 2026

Copy link
Copy Markdown
Owner

@codex review

@hathach

hathach commented Jun 4, 2026

Copy link
Copy Markdown
Owner

@claude review

@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @hathach's task in 3m 33s —— View job


Code Review: dwc2 postfixes

  • Gather context
  • Read changed file and diff
  • Analyze changes
  • Post review findings

Summary

The PR fixes EP0 SETUP handling in DWC2 DMA mode: the hardware always buffers up to 3 back-to-back SETUP packets before firing SETUP_PHASE_DONE, so the buffer must hold all 3, and the code must read the last one via DOEPDMA0. Overall the approach is sound and matches the DWC2 manual's Buffer-DMA guidance.


Issues

1. doeptsiz clears PKTCNT and XFRSIZ (raised by Copilot — likely OK, worth confirming)

src/portable/synopsys/dwc2/dcd_dwc2.c:147

// new
dwc2->epout[0].doeptsiz = (3 << DOEPTSIZ_STUPCNT_Pos);   // PKTCNT=0, XFRSIZ=0
// old
dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);

The DWC2 databook distinguishes SETUP packet reception (controlled by STUPCNT) from DATA/STATUS phase reception (controlled by PKTCNT/XFRSIZ). For SETUP packets specifically in Buffer DMA mode, the hardware uses STUPCNT to decide how many packets to accept and automatically advances DOEPDMA0 by 8 bytes per packet — PKTCNT/XFRSIZ are only decremented during DATA/STATUS phases. So zeroing them here is likely correct.

That said, Linux's dwc2_hsotg_enqueue_setup() explicitly sets PKTCNT=3, XFRSIZ=24 alongside STUPCNT=3. If this PR works in testing (which it appears to), the delta is harmless, but it may be worth matching the reference implementation to avoid surprises on edge-case cores.

Suggestion: either add (3 << DOEPTSIZ_PKTCNT_Pos) | (24 << DOEPTSIZ_XFRSIZ_Pos) to be safe, or add a brief comment confirming PKTCNT/XFRSIZ=0 is intentional for SETUP-only reception.


2. No bounds check before epout0->doepdma - 8Fix this →

src/portable/synopsys/dwc2/dcd_dwc2.c:1034

tusb_control_request_t *setup_packet = (tusb_control_request_t *) (epout0->doepdma - 8);

If setup_phase_done fires, the hardware guarantees at least one SETUP packet was received and DOEPDMA0 >= base + 8. However, a defensive guard (a TU_ASSERT or a clamp) would catch hardware-side misbehavior on poorly-spec-compliant cores and makes the constraint explicit:

TU_ASSERT(epout0->doepdma >= (uintptr_t)_dcd_usbbuf.setup_buffer + 8, );
tusb_control_request_t *setup_packet = (tusb_control_request_t *) (epout0->doepdma - 8);

3. wLength endianness — pre-existing, but worth noting

src/portable/synopsys/dwc2/dcd_dwc2.c:1038

if (setup_packet->wLength == 0) {

wLength is USB LE16. On big-endian targets this comparison would fail for non-zero lengths. The old code had the same pattern via the union member, so this is pre-existing. tu_le16toh() would make it correct for all targets:

if (tu_le16toh(setup_packet->wLength) == 0) {

Positive Changes

  • Correct buffer sizingDWC2_SETUP_BUFFER_SIZE macro properly gates 24 vs 8 bytes depending on whether DMA is enabled; slave-only builds keep the old 8-byte footprint (+16 bytes of .bss for DMA builds is expected and justified)
  • DOEPDMA0 for latest packet — correct per the manual; avoids the race between the CPU reading slot 0 and the hardware potentially having overwritten it with packet 2 or 3
  • sizeof() for dcache invalidation — cleaner and automatically correct for both DMA and non-DMA sizes
  • Dropped union/setup_packet overlay — the old typed alias was misleading once the buffer grew to 3 slots; using a pointer derived from doepdma is the right abstraction
  • dwc2 local variable reuse — using the existing dwc2 pointer instead of calling DWC2_REG(rhport) twice is a minor cleanup

The core logic is correct and the test evidence (3/4 back-to-back SETUP packets handled) gives confidence. Items 1 and 2 are worth addressing before merge.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5e080732e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Receive only 1 packet
dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
// Receive back-to-back setup packets
dwc2->epout[0].doeptsiz = (3 << DOEPTSIZ_STUPCNT_Pos);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore EP0 transfer size when arming DMA setup

On internal-DMA DWC2 parts this arms EP0 with XFRSIZ=0 and PKTCNT=0, because the assignment now writes only STUPCNT. The DWC2/STM32 EP0 OUT start sequence still needs a nonzero packet count and transfer size for the setup DMA buffer (ST's USB_EP0_OutStart() programs PKTCNT=1, XFRSIZ=3*8, and STUPCNT); otherwise the core is enabled for a zero-length OUT transfer and can fail to DMA/complete the first SETUP packet, breaking enumeration in DMA mode.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment thread src/portable/synopsys/dwc2/dcd_dwc2.c
Comment thread src/portable/synopsys/dwc2/dcd_dwc2.c
Cast DOEPDMA0 through uintptr_t and use sizeof(tusb_control_request_t)
instead of the magic constant 8, matching project convention. Add a
reference to Programming Guide v4.20a 9.1.2.1 for the DOEPDMAn-8 rule.

Addresses Copilot review comment; no functional change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hathach

hathach commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Review triage (Codex + Copilot)

Thanks for the reviews. Cross-checked each against the Synopsys DWC2 USB 2.0 HS-OTG Databook / Programming Guide v4.20a.

P1 — dma_setup_prepare() writes only STUPCNT, dropping PKTCNT/XFRSIZ (Codex + Copilot)not changing.
The SETUP-reception arming sequence in the Programming Guide requires only SUPCnt:

  • §9.1.2.1 Control Setup Transactions → Application Programming Sequence: "Program the DOEPTSIZn register. DOEPTSIZn.SUPCnt = 3" (no PktCnt/XferSize).
  • §7.4.2 Initialization on Enumeration Completion: SETUP reception needs SUPCnt, DOEPDMA0, and EPENA only.

The SETUP DMA write to memory is governed by SUPCnt + EPENA, not by PktCnt/XferSize. ST's USB_EP0_OutStart() programs all three defensively, but it is not a spec requirement. This was also hardware-verified by @HiFiPhile (3/4 back-to-back SETUP test). So the zero-length-OUT concern does not apply to SETUP arming.

wLength endianness (Copilot)not an issue. The comparison is wLength == 0, which is byte-order independent (0 == 0 regardless of endianness).

DOEPDMA0 - 8 underflow (Copilot)unreachable. At setup_phase_done the core has advanced DOEPDMA0 by ≥ 8 per received SETUP packet (it points past the last write), so the result stays within setup_buffer. This is exactly the documented rule (PG §9.1.2.1: "DOEPDMAn-8 provides the pointer to the last valid SETUP data").

Pointer cast / magic 8 (Copilot) — ✅ applied in a900ea93d: cast through uintptr_t and use sizeof(tusb_control_request_t) instead of the literal 8, matching project convention.

@hathach hathach left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfect, thank you @HiFiPhile . Sorry for the noise with AI bot, I am testing it out with non-fokred contributor to see if it works (may only work with my own pr). These bots can be useful for trivial fix and testing.

PS: My hil pool is growing large and seem like either kernel usb has issues with large amount of devices at the same time or I got issue with power (overcurrent), re-run hil a few times with less active boards works.

@hathach hathach merged commit 38debe0 into master Jun 4, 2026
426 of 428 checks passed
@hathach hathach deleted the dwc2_postfix branch June 4, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants