Skip to content

Conversation

ithinuel
Copy link
Contributor

@ithinuel ithinuel commented Jan 17, 2021

Windows usb host do not wait for the whole descriptor to be send before moving to the set_address phase.
Instead it enters the status stage by emitting a ZLP on EP0 Out (Status Out) instead of sending the expected DATA IN request.

This can be reproduced by using a max_packet_size_0 smaller than the device descriptor (typically 18bytes).
UsbDeviceBuilder uses a default value of 8 which triggers the issue.

The attached logic analyser trace demonstrates that : small_ctrl_packet.zip (requires saleae logic 2)

Solution in other usb stacks

The linux Kernel

The linux kernel's usb gadget stack accepts a transition to the status phase at anytime during an IN request by priming the OUT EP before starting to process the IN request.

Mbed-OS

Mbed-OS' usb stack stalls the endpoint 0 if it receives an Out packet while processing transfer in the Receive direction.
See:

  • USBDevice::_control_out returns false if it's processing a transfer in a Receive direction.
  • USBDevice::ep0_out stalls the endpoint 0 if _control_out returns false.

TinyUsb

TinyUsb hacks the length fields of the request if the descriptor does not fit the endpoint's buffer and if the address is not yet set (aka at the start of the enumeration).

Proposed solution

Accept a ZLP on EP0 OUT during a DataIn transfer as the signal to enter the Status stage of the transfer.

Related issues

@ithinuel
Copy link
Contributor Author

ithinuel commented May 15, 2021

@mvirkkunen Merge conflict fixed.

@ryan-summers
Copy link
Member

Hmm, it appears you fixed the bug long before I dove into this. We merged a partial fix in #83, but this PR has further improvements as it also handles the DataZlp state. I'd be happy to merge this one as well to improve support further :)

@ithinuel
Copy link
Contributor Author

I didn't manage to convince @mvirkkunen this was an appropriate fix back in Sep 2020 :D

Copy link
Member

@ryan-summers ryan-summers left a comment

Choose a reason for hiding this comment

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

I've personally also debugged this issue through the Windows USB stack. On Linux, it turns out this wasn't a major problem because the host issued a bus reset that unstalled our endpoint. However, Windows isn't as nice, so EP0 can get into a permanently stalled state and the bus isn't reset to get out of that state, which causes enumeration to fail.

I don't know if DataZlp is strictly necessary, but it seems like a more comprehensive means of addressing the issue of handling these packets in unexpected states.

@ryan-summers ryan-summers merged commit 413b77c into rust-embedded-community:master May 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants