Raspberry Pi Pico Host bulk transfers: Is there a better way? #1261
-
SummaryThe way I implemented bulk endpoints for the RP2040 MIDI Host (pull request #1219) is a hack. I describe at length what I did and why because I am hoping someone will tell me the hack is not required because there is a better way. If you have a better way, or can tell me I am on the right track, I would appreciate it. The ProblemThe RP2040 USB IP in host mode provides dedicated hardware for each interrupt endpoint. However, it offers just one endpoint hardware block to use for all control transfers, bulk transfers, and isochronous transfers. The host controller software controls how to share this endpoint hardware among concurrent functions. For a MIDI device, that is typically one bulk IN endpoint and one bulk OUT endpoint. This is my understanding of the RP2040 Host hardware operation: Every time the RP2040 USB host software needs to start a bulk IN or bulk OUT transfer, it sets up the shared endpoint control register and shared endpoint buffer register, and then writes to bits in the SIE Control register to start the transfer. The endpoint hardware retries this transfer forever until it completes or there is an error. NAK is not an error. This is fine for control IN transfers because they are always setup command and response. You know that IN transfers will either STALL, timeout or complete. This is also fine for control OUT and bulk OUT transfers because the device can't NAK forever. It either has to accept the data sometime, or it has to STALL or timeout. Bulk IN transfers are the problem. The host can request data from a MIDI keyboard. That keyboard may not send anything until the player presses a key. Meanwhile, the host can't send data to the keyboard. Some MIDI devices will always send something (even a buffer full of 0) in response to an IN token, but most will NAK until something happens that requires the device to send something (e.g., the user presses a key or button, turns a knob, moves a slider, etc., or enough time has passed for a new active sensing message or MIDI clock event event). The automatic NAK retry behaves like a bug in this case, not a feature. My Ideal SolutionGiven how the hardware works, my ideal algorithm for sharing the host endpoint between bulk IN and bulk OUT transfers is:
What I wanted for bulk IN was a way to turn off automatic retry on NAK and instead get an interrupt on NAK. I could find no way to do that with the hardware registers. There is no documented way to stop the NAK auto-retry. The Solution Needs a Hack
Aborting the IN TransferThis is the hack. In pull request #1219 it is coded in file Text Description
Code
Other Issues
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
I know it's an old topic but I would suggest that you check if your DATA0/1 PID order is correct, I believe that the current host pico driver layer implementation will not remember DATA pid of the endpoint when switching between endpoints on EPx. This is actually a pretty big issue, NAK will be usually issued when the device is busy doing sth and USB Controller will retry until it can finalize the transaction. Try to connect an MSC device and check if you can get the data about flash driver (capacity). It's a similar case, and the host PICO will stay stucked waiting for data sending NAKed packets. |
Beta Was this translation helpful? Give feedback.
-
@conkerkh Thanks for the reply. The changes I made to hcd_rp2040.c in my pull request allocate a new hw endpoint structure for each bulk endpoint. The toggle bit is tracked there. It seems right, but I don't have a USB Protocol Analyzer to check for sure. The code I have seems to work OK for me and for other people except if the transfer size is not 64 bytes. There was no bulk endpoint support for the RP2040 when I submitted my pull request, so if MSC is supported now, it is probably implementing bulk transfers differently. |
Beta Was this translation helpful? Give feedback.
-
There is a solution. See pull request #1434. There seems to be no need to directly share EPx. I must have misread the documentation. |
Beta Was this translation helpful? Give feedback.
-
One way that might be less hacky: When we want to start a bulk IN transfer, set the Inside the interrupt handler, if we detect the NAK case, always cancel the retry using The real issue is that there seems to be no way for software to query whether the EPX endpoint is currently active. Otherwise, we could just let hardware retry with a shorter Edit: Never mind, the “Interrupt on NAK” flag of doesn't exist in host mode. I also don't see another way to get an interrupt when |
Beta Was this translation helpful? Give feedback.
There is a solution. See pull request #1434. There seems to be no need to directly share EPx. I must have misread the documentation.