Debugging USB with TinyFPGA BX
I wanted to write a note on a quick debug session to get my TinyFPGA BX board
working with my T450s running Debian Sid (unstable). The board doesn’t come
with a separate USB controller (typically a FTDI chip or a microcontroller
running a USB stack). Instead it loads a bootloader design from its QSPI flash
which then presents a CDC class USB device to the host computer which shows up
as a /dev/ttyACMn
device.
When connected to a USB host - detected by a USB Start of Frame (SoF) packet
the bootloader waits for host commands without exiting. This is indicated by
blinking the onboard LED. When I plugged the device in, however, the LED came
on and blinked for all of 4 seconds before turning off at which point the
device disconnected. I tried using TinyFPGA’s tinyprog
programming tool after
resetting the board only to discover another process holding the device node
file open.
This turned out to be ModemManager (thanks lsof
) a Network Manager
sub-program designed to talk to USB modems using AT commands. The bootloader
doesn’t seem to like this and exits causing the device to “disconnect”. After
stopping and disabling the modemmanager
service via systemctl
I was hoping
that it would fix my bootloader troubles but unfortunately that didn’t seem to
have made a difference. The bootloader LED still turned off after blinking for
about 4-5 seconds followed by the device disconnecting with the following log
messages in dmesg
:
usb 2-1: New USB device found, idVendor=1209, idProduct=2100, bcdDevice= 0.00
usb 2-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
cdc_acm 2-1:1.0: ttyACM0: USB ACM device
usb 2-1: USB disconnect, device number 9
I wasn’t sure if the fault lay with the board, the USB cable, the laptop hardware (USB host controller) or with the host software. I tried plugging in the device into a Ubuntu 18.04 desktop to see if my board worked and it did! That eliminated the board as a possible suspect. The next experiment I did was to disable USB driver autoprobing and see if the bootloader still exited after a few seconds. Surprisingly, this time around the LED kept happily blinking away! This eliminated the cable and host USB hardware from the running. The problem lay with the interaction of the system software and the port.
The next step took a little while but Google led me to usbmon
which
is a Linux kernel module for sniffing USB packets. Since I now had a working
case (driver_autoprobe
set to 0) and a non-working case I could compare the
packets transmitted in both cases to try to figure out what went wrong in the
latter. Although usbmon
exposes a text interface via debugfs
which captures
raw transmitted USB packets for each bus I found it was much easier to use
Wireshark (which I was surprised to find works for USB packet sniffing too!).
$ grep CONFIG_USB_MON /boot/config-$(uname -r) && modprobe usbmon
$ sudo wireshark &
At this point we can start sniffing USB packets once we know the bus that our
device is on which we can figure out by looking the dmesg
output. For
example, in the dmesg
snippet above the device is on bus 2 which means we
need to look at transactions over usbmon2
in Wireshark.
Some more digging and a searching revealed that something was suspending power to the port that the board was connected to. That something then turned out to be TLP - a power management daemon for Thinkpads that can autosuspend USB ports (runtime power management) when they are inactive.
Fortunately, there is an option to exclude specific USB devices from TLP’s
runtime power management based on their vendor and product ID tuple. I added
the TinyFPGA’s VID:PID under USB_BLACKLIST
in /etc/default/tlp
and
restarted the TLP service for good measure and the bootloader LED happily
blinked away!