USB, UDEV and Identical Devices with No Serial Numbers

I admit it. I have a bit of a HiFive habit. For those not in the know, "HiFive" is the name of a family of single board computers featuring RISC-V processors. The HiFive1 looks somewhat like an Arduino and features a 32-bit "embedded" SoC. The HiFive Unleashed is it's beefier cousin. It features a 64-bit penta-core system with a bunch of DRAM and runs Linux.

But they both have a couple things in common. Firstly, they both use the same FTDI USB to UART chip. Secondly, it's my job to support both of them, so I frequently have two (or more) of these devices plugged in to my Linux system at the same time.

This is a problem because they don't report serial numbers during enumeration, making it hard to assign each board to its own unique, human-memorable device file name. In other words, I want the HiFive1 to show up as /dev/hifive1 when I plug it in and the HiFive Unleashed to show up as /dev/hifiveu. But if you do a bit of googling on udev (Linux's userland device subsystem) you can find plenty of examples for how to do this... if the devices report unique serial numbers.

After a bit of experimentation and reading, I discovered it was possible to assign human memorable device names based on which port you plugged it into. This isn't an ideal situation, but it's good enough. Here's what I did:

First, install the 99-openocd.rules file as described in the HiFive1 or HiFive Unleashed getting started guide (links below.) This essentially involves copying this udev "rules file" into /etc/udev/rules.d/99-openocd.rules:

SUBSYSTEM=="usb",ATTR{idVendor}=="0403",ATTR{idProduct}=="6010",MODE="664",GROUP="plugdev"

SUBSYSTEM=="tty",ATTRS{idVendor}=="0403",ATTRS{idProduct}=="6010",MODE="664",GROUP="plugdev"

And copy the following lines into the file /dev/udev/rules.d/60-import.rules if it's not there already:

SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id_tag"

Now plug in your first device. You should see two devices appear: /dev/ttyUSB0 and /dev/ttyUSB1. The first is the device used by OpenOCD to communicate with the JTAG interface on the board while the second is the interface to the device's UART. That's the one I want to hide behind a unique device name.

Type in the command:

udevadm info -q all /dev/ttyUSB1 | grep ID_PATH_TAG

You should get a response something like this:

E: ID_PATH_TAG=pci-usb-0_1_1_2_1_1

It may not be exactly the same, but as long as it's unique for each USB port on your device, it's fine. Now go back to the 99-openocd.rules file and add this to the end of the line that starts with SUBSYSTEM="tty":

,SYMLINK+="sifive_$env{ID_PATH_TAG}"

Now plug your device back in and look at newly formed devices in /dev:

ls -l /dev/sifive_*

You should see a device that looks like this:

lrwxrwxrwx 1 root root 7 Aug 16 18:26 /dev/sifive_pci-usb-0_1_1_2_1_1 -> ttyUSB1

It's okay if you see multiple "sifive" devices, all we care about is that they're unique and map to a particular hardware USB port. Now that we've told the udev subsystem to create a unique link for tty devices that plug into a particular port, we can use that name as a target for a more human memorable name by manually creating a symlink.

In this example, I connected my HiFive1 to this port, so I created a "hifive1" symbolic link to this first device:

ln -s sifive_pci-0000_39_00_0-usb-0_1_1_2_1_1 hifive1

Now as long as I plug my HiFive1 into the same port, it's serial interface will show up as /dev/hifive1.

You can repeat this process with other HiFive1 devices, perhaps naming them /dev/hifive1-1 or /dev/hifive1-2 and so on or add other symbolic links for the HiFive Unleashed board (which also uses the same FTDI chipset.)

References

  1. HiFive1 Getting Started Guide
  2. HiFive1 Product Information Page
  3. HiFive Unleashed Product Information Page
  4. Wikipedia's page on Linux udev