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