Skip to content
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

usb: device_next: uac2: Double buffering on IN data endpoints #82135

Merged
merged 4 commits into from
Nov 30, 2024

Conversation

tmon-nordic
Copy link
Contributor

Application is expected to call usbd_uac2_send() on each enabled USB Streaming Output Terminal (isochronous IN data endpoint) exactly once every SOF. The class is bookkeeping queued transfers to make it easier to determine component at fault when things go wrong. However, this approach only works fine if the underlying USB device controller buffers
the data to be sent on next SOF and reports the transfer completion immediately after data is buffered (e.g. nRF52 USBD).

While DWC2 otg also requires the SW to arm endpoint with data for the next SOF, unlike nRF52 USBD the transfer is only considered complete after either the IN token for isochronous endpoint is received or after the Periodic Frame Interval elapses without IN token. This design inevitably requires the application to be able to have at least two buffers for isochronous IN endpoints.

Support dual buffering on IN data endpoints to facilitate sending isochronous IN data on every SOF regardless of the underlying USB device controller design.

The NAKSts bit may be set on isochronous OUT endpoints when incomplete
ISO OUT interrupt is raised. The code would then assume that endpoint is
already disabled and would not perform the endpoint disable procedure.
This in turn was essentially halting any transmission on the isochronous
endpoint, abruptly terminating the data stream.

Fix the issue by always following full endpoint disable procedure on
isochronous endpoints.

Signed-off-by: Tomasz Moń <[email protected]>
When handling incomplete iso IN interrupt mark current transfer as
complete and program the endpoint with any subsequently queued packet.
Program the endpoint directly in interrupt handler because the data
must be programmed before SOF (by the time incomplete iso IN interrupt
is raised there is less than 20% * 125 us = 25 us before SOF).

Signed-off-by: Tomasz Moń <[email protected]>
@zephyrbot zephyrbot added the area: USB Universal Serial Bus label Nov 27, 2024
@rick1082 rick1082 self-requested a review November 28, 2024 09:51
Application is expected to call usbd_uac2_send() on each enabled USB
Streaming Output Terminal (isochronous IN data endpoint) exactly once
every SOF. The class is bookkeeping queued transfers to make it easier
to determine component at fault when things go wrong. However, this
approach only works fine if the underlying USB device controller buffers
the data to be sent on next SOF and reports the transfer completion
immediately after data is buffered (e.g. nRF52 USBD).

While DWC2 otg also requires the SW to arm endpoint with data for the
next SOF, unlike nRF52 USBD the transfer is only considered complete
after either the IN token for isochronous endpoint is received or after
the Periodic Frame Interval elapses without IN token. This design
inevitably requires the application to be able to have at least two
buffers for isochronous IN endpoints.

Support dual buffering on IN data endpoints to facilitate sending
isochronous IN data on every SOF regardless of the underlying USB device
controller design.

Signed-off-by: Tomasz Moń <[email protected]>
jfischer-no
jfischer-no previously approved these changes Nov 29, 2024
@jfischer-no jfischer-no added area: Drivers Experimental Experimental features not enabled by default labels Nov 29, 2024
udc_submit_ep_event(dev, buf, 0);

/* Try to queue next packet before SOF */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just found out that there is a race condition between the host, DWC2 otg core and SW. After incomplete iso IN is triggered, it is still possible for the IN to be sent and actually handled. The failed race condition here that results in double pulling data from net buf is as follows:

  1. Incomplete iso IN is raised
  2. if ((diepctl & mask) == val) condition above is true, driver takes path to call udc_dwc2_ep_disable()
  3. Before udc_dwc2_ep_disable() finishes, the host sends the IN token, IN transfer complete interrupt is raised
  4. Driver executes udc_dwc2_ep_disable()
  5. Driver submits next transfer here
  6. Host sends SOF
  7. IN transfer complete from point 3 is handled but it was for the previous transfer! The net buf is pulled the last tx data
  8. Host sends IN token, device responds and IN transfer complete interrupt is raised
  9. Driver on next isr loop (without reentering ISR, there is a while() in ISR handler until all interrupts are handled) notices IN transfer complete. The net buf is pulled the last tx data (second time, which triggers assertion failure)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The workaround is to clear XferCompl interrupt on endpoint disable. This seems to solve not only the IN problem, but also occassional "udc_dwc2: No buffer for ep" for OUT endpoints.

@tmon-nordic tmon-nordic added the DNM This PR should not be merged (Do Not Merge) label Nov 29, 2024
Endpoint disable function is racing against bus traffic. If the bus
traffic leads to transfer completion immediately before the endpoint
disable is executed, then the transfer complete interrupt will remain
set when the endpoint is disabled. For OUT endpoints this leads to "No
buffer for ep" errors, while for IN endpoint this can lead to double
buffer pull which causes assertion failure.

The proper solution would be to change endpoint disable to not actually
wait for the individual events (and accept that the endpoint may not
need to be disabled because the transfer can just finish). For the time
being workaround the issue by clearing XferCompl bit on endpoint
disable.

Signed-off-by: Tomasz Moń <[email protected]>
@tmon-nordic tmon-nordic removed the DNM This PR should not be merged (Do Not Merge) label Nov 29, 2024
@kartben kartben merged commit a26d3c2 into zephyrproject-rtos:main Nov 30, 2024
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Drivers area: USB Universal Serial Bus Experimental Experimental features not enabled by default
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants