rp2040 host: fix SET_REPORT (and any OUT-data control xfer) sending DATA0 instead of DATA1#3644
Merged
Conversation
…ATA0 instead of DATA1 hcd_edpt_xfer() previously reset ep->next_pid to 1 only when the control endpoint direction changed between stages. That handled IN-data control transfers (e.g. GET_REPORT, GET_DESCRIPTOR) where SETUP is OUT and DATA is IN, but not OUT-data class requests like SET_REPORT, where SETUP and DATA are both OUT and the direction-change check is false. ep->next_pid was left at 0 from hcd_edpt_open(), so the DATA stage went on the wire as DATA0 when the device expected DATA1. Strict devices (observed: Elgato Stream Deck) treat this as a protocol violation and disconnect. Key off "endpoint 0" instead of "direction changed", restoring the previous behavior. Interrupt/bulk endpoints take the ep->interrupt_num > 0 branch above and never reach this code, so they are unaffected.
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a host-mode bug in the RP2040 HCD where SET_REPORT (and other class control transfers with an OUT data stage) sent the DATA stage as DATA0 instead of DATA1, causing strict devices to disconnect. The previous logic only reset next_pid to 1 when the control endpoint direction flipped between stages, which never happens for OUT-data control transfers since both SETUP and DATA share ep_addr == 0x00.
Changes:
- Replace the "direction changed" check with an "endpoint 0" check so
next_pidis reset to 1 on every control transfer stage. - Add a detailed comment explaining the USB 2.0 §8.5.3 requirement and the SET_REPORT failure mode.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(disclaimer: the fix and the explanation were prepared by Claude, but it fixed a problem on my device after upgrading to latest tinyusb)
Summary
In
hcd_edpt_xfer()(src/portable/raspberrypi/rp2040/hcd_rp2040.c),ep->next_pidis reset to 1 only when the control-endpoint direction changes between stages. That works for IN-data control transfers (GET_REPORT, GET_DESCRIPTOR, …) where SETUP is OUT and DATA is IN — the direction flips and the data stage correctly goes out as DATA1. It does not work for class requests with an OUT data stage like SET_REPORT, where both SETUP and DATA are OUT (ep_addr == 0x00for both): theep_addr != ep->ep_addrcheck is false,ep->next_pidstays at 0 fromhcd_edpt_open(), andbufctrl_prepare16()then sends the data stage as DATA0.Per USB 2.0 §8.5.3, every stage after SETUP must start with DATA1. A DATA0 OUT in the data stage of a SET_REPORT, when the device expected DATA1, is a protocol violation. Strict devices respond by tearing down the connection.
Repro
Failing log (annotated):
GET_REPORTworks because OUT→IN direction change resetsnext_pidto 1.SET_REPORT(and any other OUT-data class control request) does not.Fix
Key off "endpoint 0" instead of "direction changed", matching the earlier (working) behavior of this code. The control transfer's data and status stages both always start with DATA1 regardless of whether the direction has changed since the previous stage, so reset
ep->next_pidto 1 every timehcd_edpt_xferis invoked on ep 0.Interrupt and bulk endpoints take the
ep->interrupt_num > 0branch above and never reach this code, so they are unaffected.Proof the regression is fixed
Same SET_REPORT call after the patch — DATA stage goes out as DATA1, STATUS stage completes, device stays attached: