November 18, 2023

Let's learn USB! -- Working towards an all C driver

This has been my goal from the start, so it is no reflection on the papoon code I have been working with. Papoon has been a stepping stone, and opportunity to get "hand on" a bit with C++, and to confirm my preference for simple C code.
So my efforts are now going on here: Obviously "baboon" is inspired by pondering upon words that rhyme with "papoon".

This move is also a milestone of sorts, and a turning point. It is a transition from studying and learning to writing my own code. Of course plenty more learning will take place while doing that. But that also means less effort spent here writing text and more time spent writing code.

Serial ports are slow

On several occasions I have learned and relearned that trying to use printf during, for example, enumeration causes enough delay to cause the protocol to fail. Consider a serial port running at 115200 baud. At one time this was an unthinkably fast bit rate for a serial port, but it is the new normal. If we use the common rule of thumb of 10 bits per character (which allows for some overhead), this gives us 11,520 characters per second. This still may seem like a lot, but consider -- this is 11.52 characters per millisecond.

USB requires certain things to happen with fairly tight timing. A device needs to respond to a RESET in 10 ms. Other timings require a response within 5 ms.

The basic lesson I have learned is to not place any printf statements within USB interrupt handling routines. I have written some debug and analysis code that captures enumeration traffic in a sort of log, then displays it when enumeration has finished.

Tests and experiments

I let papoon still handle enumeration, then use endpoints 2 and 3 for experiment with serial communication, not using papoon code at all, but directly accessing the USB hardware.

I found I could send a single character just fine. Then I set up a test sending single characters back to back as rapidly as possible. To do this, I let the CTR (correct transfer interrupt) for the sending of one character trigger the sending of the next. Doing this, I sent 500,000 characters and used a stopwatch to time the overall transfer. It took 7 seconds. This is 71,000 characters per second (something like you might get from 710,000 baud). This is also 71 characters per millisecond, which surprises me. We get SOF interrupts every millisecond, and with single character transfers, this means we are sending 71 characters in one frame time. How can this be. Note also that 71 is pretty close to 64 and given the accuracty of stopwatch timing, 64 characters per millisecond could be the truth.

I was also able to code the "echo loop" to run entirely in my own C code, so at this point I am only relying on papoon to handle enumeration.

I have seen two odd things with the ACM driver.

The first is that trying to send 2 characters in a write just fails. picocom never sees a thing. Wireshark shows that there are two character payloads in USB "URB" packets. I don't know what this means. Perhaps the ACM protocol dictates single character packets. The issue may be in the linux ACM drive, and it isn't really an "issue" if the protocol is specified only for single character packets.

The second thing is that after I tell picocom to disconnect, if I restart picocom, the connection is dead. I don't know what to say about this.

I am curious if both these issues change if I set up my enumeration to "lie" and claim that I am a CP2102 or some such device that will get linux to use the ttyUSB driver. We won't know until we try.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org