-
Notifications
You must be signed in to change notification settings - Fork 7k
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
Conversation
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]>
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]>
e88aa82
to
d55cc46
Compare
udc_submit_ep_event(dev, buf, 0); | ||
|
||
/* Try to queue next packet before SOF */ |
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 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:
- Incomplete iso IN is raised
if ((diepctl & mask) == val)
condition above is true, driver takes path to calludc_dwc2_ep_disable()
- Before
udc_dwc2_ep_disable()
finishes, the host sends the IN token, IN transfer complete interrupt is raised - Driver executes
udc_dwc2_ep_disable()
- Driver submits next transfer here
- Host sends SOF
- IN transfer complete from point 3 is handled but it was for the previous transfer! The net buf is pulled the last tx data
- Host sends IN token, device responds and IN transfer complete interrupt is raised
- 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)
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.
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.
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]>
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.