How to distinguish between identical USB-to-serial adapters?

90,079

Solution 1

is there any way I can write a udev rule that fixes the name of each adapter based on which physical port on the hub the adapter is plugged into?

Yes there is, as it turns out. Consider the last portion of the device hierarchy shown in the second example above:

looking at parent device '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5': KERNELS=="1-4.5"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="69"
ATTRS{idVendor}=="067b"
ATTRS{idProduct}=="2303"
ATTRS{bcdDevice}=="0300"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="12"
ATTRS{busnum}=="1"
ATTRS{devnum}=="7" ATTRS{version}==" 1.10" ATTRS{maxchild}=="0" ATTRS{quirks}=="0x0"
ATTRS{authorized}=="1"
ATTRS{manufacturer}=="Prolific Technology Inc."
ATTRS{product}=="USB-Serial Controller"

The name given to this device by the kernel (KERNELS=="1-4.5") indicates that this device is plugged into the fifth port of a hub connected to port four on bus 1 (see this FAQ for more information on how to decode the sysfs usb device hierarchy). With some help from this guide to writing udev rules I came up with the following set of udev rules for my USB-to-serial-port converters:

KERNEL=="ttyUSB*", KERNELS=="1-8.1.5", NAME="ttyUSB0"
KERNEL=="ttyUSB*", KERNELS=="1-8.1.6", NAME="ttyUSB1"
KERNEL=="ttyUSB*", KERNELS=="1-8.1.1", NAME="ttyUSB2"
KERNEL=="ttyUSB*", KERNELS=="1-8.1.2", NAME="ttyUSB3"

These rules have one obvious disadvantage: they assume that all USB-to-serial converters will be plugged into the same hub ("1-8.1.*"). If a USB to serial converter was plugged into another USB port it could be assigned the name "ttyUSB0" which would conflict with the naming scheme described above. However, since I leave all of the converters plugged into the hub I can live with this constraint.

Solution 2

Although it would not help in this specific case, some adapters are assigned unique serial ids:

udevadm info -a -n /dev/ttyUSB1 | grep '{serial}'

An example adapter serial id:

  ATTRS{serial}=="A6008isP"`

and udev rules would then contain:

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A6008isP", SYMLINK+="arduino"

Source

Solution 3

Have you looked at the contents of /dev/serial/by-id/? In a similar situation each device was assigned a unique persistent ID there (I'll admit don't know what it actually represents).

Solution 4

Since the original question was asked 3 years ago, this might not adress the asker, but I will post it for future reference.

There is a way to reprogramm the Serial-Number by accessing the EEPROM of the FTDI-Chips, Silicon labs provides a tool, but it is Windows only:

Product page->Tools->Fixed Function Customization Utility

Direct link

An instruction can be found at remotehq:

http://remoteqth.com/wiki/index.php?page=How+to+set+usb+device+SerialNumber

There is also a Unix library on Sourceforge. It is only tested with CP2101 / CP2102 / CP2103 and I did not try it out personally.

http://sourceforge.net/projects/cp210x-program/

Solution 5

Using an answer rather than a comment as I need formatting.

These rules have one obvious disadvantage: they assume that all USB-to-serial converters will be plugged into the same hub ("1-8.1.*"). If a USB to serial converter was plugged into another USB port it could be assigned the name "ttyUSB0" which would conflict with the naming scheme described above. However, since I leave all of the converters plugged into the hub I can live with this constraint.

I had this issue and it's easily fixed by using a small C program to manipulate the text of %devpath or some other USB attribute of your choosing.

You then call that program like this:

ACTION!="add|change", GOTO="99-local-end

SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GOTO="99-local-tty-ftdi"
GOTO="99-local-end"

LABEL="99-local-tty-ftdi"
IMPORT{program}="/usr/local/lib/udev/multiusbserial-id %s{devpath}"
# Hayes-style Modem
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="1", GROUP="dialout", MODE="0660", SYMLINK+="modem"
# Console for network device
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="2", GROUP="wheel", MODE="0660", SYMLINK+="ttyswitch"
# Serial port for software development
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="3", GROUP="eng", MODE="0660", SYMLINK+="ttyrouter"
# Unused
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="4", GROUP="wheel", MODE="0660"

LABEL="99-local-end"

where multiusbserial-id is the compiled C program.

The program just needs to print text after a particular point, so it isn't complex

/* multiusbserial.c */
#include <stdio.h>
#include <stdlib.h>

#define PROGRAM_NAME "multiusbserial-id"
#define VARIABLE_PREFIX "ID_MULTIUSBSERIAL_"

int main(int argc, char *argv[])
{
  char *p;
  int found = 0;

  if (argc != 2) {
    fprintf(stderr, "Usage: " PROGRAM_NAME " ATTRS{devpath}\n");
    exit(1);
  }

  for (p = argv[1]; *p != '\0'; p++) {
    if (*p == '.') {
      p++;
      found = (*p != '\0');
      break;
    }
  }

  if (!found) {
    fprintf(stderr, PROGRAM_NAME ": unexpected format\n");
    exit(1);
  }

  printf(VARIABLE_PREFIX "DEVNAME_MINOR=%s\n", p);
  return 0;
}

I wrote a blog article with more details. It's one of a series in setting up an embedded systems team programming environment.

Share:
90,079

Related videos on Youtube

Chris OBrien
Author by

Chris OBrien

Updated on September 18, 2022

Comments

  • Chris OBrien
    Chris OBrien over 1 year

    I use a number of identical USB-to-serial adapters with my laptop (Ubuntu 9.10). The adapters are manufactured by Sabrent and are built around a Prolific PL2303 IC, as shown by lsusb:

    Bus 001 Device 008: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port  
    Bus 001 Device 007: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port  
    Bus 001 Device 006: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port  
    

    None of the attributes displayed by udevadm seem to be unique to a particular adapter:

    foo@bar:~$ udevadm info --attribute-walk --path=/sys/bus/usb-serial/devices/ttyUSB0
    
       looking at device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1/1-4.1:1.0/ttyUSB0':  
         KERNEL=="ttyUSB0"  
         SUBSYSTEM=="usb-serial"  
         DRIVER=="pl2303"   
         ATTR{port_number}=="0"  
    
       looking at parent device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1/1-4.1:1.0':
         KERNELS=="1-4.1:1.0"  
         SUBSYSTEMS=="usb"  
         DRIVERS=="pl2303"  
         ATTRS{bInterfaceNumber}=="00"  
         ATTRS{bAlternateSetting}==" 0"  
         ATTRS{bNumEndpoints}=="03"  
         ATTRS{bInterfaceClass}=="ff"  
         ATTRS{bInterfaceSubClass}=="00"  
         ATTRS{bInterfaceProtocol}=="00"  
         ATTRS{modalias}=="usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00"  
         ATTRS{supports_autosuspend}=="1"  
    
       looking at parent device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1':
         KERNELS=="1-4.1"   
         SUBSYSTEMS=="usb"  
         DRIVERS=="usb"   
         ATTRS{configuration}==""  
         ATTRS{bNumInterfaces}==" 1"  
         ATTRS{bConfigurationValue}=="1"  
         ATTRS{bmAttributes}=="80"  
         ATTRS{bMaxPower}=="100mA"  
         ATTRS{urbnum}=="538"  
         ATTRS{idVendor}=="067b"  
         ATTRS{idProduct}=="2303"  
         ATTRS{bcdDevice}=="0300"  
         ATTRS{bDeviceClass}=="00"  
         ATTRS{bDeviceSubClass}=="00"  
         ATTRS{bDeviceProtocol}=="00"  
         ATTRS{bNumConfigurations}=="1"  
         ATTRS{bMaxPacketSize0}=="64"  
         ATTRS{speed}=="12"  
         ATTRS{busnum}=="1"  
         ATTRS{devnum}=="6"  
         ATTRS{version}==" 1.10"  
         ATTRS{maxchild}=="0"  
         ATTRS{quirks}=="0x0"  
         ATTRS{authorized}=="1"  
         ATTRS{manufacturer}=="Prolific Technology Inc."  
         ATTRS{product}=="USB-Serial Controller"  
    
         <snip>
    
     foo@bar:~$ udevadm info --attribute-walk --path=/sys/bus/usb-serial/devices/ttyUSB1
    
       looking at device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5/1-4.5:1.0/ttyUSB1':
         KERNEL=="ttyUSB1"  
         SUBSYSTEM=="usb-serial"  
         DRIVER=="pl2303"  
         ATTR{port_number}=="0"  
    
       looking at parent device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5/1-4.5:1.0':
         KERNELS=="1-4.5:1.0"  
         SUBSYSTEMS=="usb"  
         DRIVERS=="pl2303"  
         ATTRS{bInterfaceNumber}=="00"  
         ATTRS{bAlternateSetting}==" 0"  
         ATTRS{bNumEndpoints}=="03"  
         ATTRS{bInterfaceClass}=="ff"  
         ATTRS{bInterfaceSubClass}=="00"  
         ATTRS{bInterfaceProtocol}=="00"  
         ATTRS{modalias}=="usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00"  
         ATTRS{supports_autosuspend}=="1"  
    
       looking at parent device
     '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5':
         KERNELS=="1-4.5"  
         SUBSYSTEMS=="usb"  
         DRIVERS=="usb"  
         ATTRS{configuration}==""  
         ATTRS{bNumInterfaces}==" 1"  
         ATTRS{bConfigurationValue}=="1"  
         ATTRS{bmAttributes}=="80"  
         ATTRS{bMaxPower}=="100mA"  
         ATTRS{urbnum}=="69"  
         ATTRS{idVendor}=="067b"  
         ATTRS{idProduct}=="2303"  
         ATTRS{bcdDevice}=="0300"  
         ATTRS{bDeviceClass}=="00"  
         ATTRS{bDeviceSubClass}=="00"  
         ATTRS{bDeviceProtocol}=="00"  
         ATTRS{bNumConfigurations}=="1"  
         ATTRS{bMaxPacketSize0}=="64"  
         ATTRS{speed}=="12"  
         ATTRS{busnum}=="1"  
         ATTRS{devnum}=="7"  
         ATTRS{version}==" 1.10"  
         ATTRS{maxchild}=="0"  
         ATTRS{quirks}=="0x0"  
         ATTRS{authorized}=="1"  
         ATTRS{manufacturer}=="Prolific Technology Inc."  
         ATTRS{product}=="USB-Serial Controller"  
    
         <snip>
    

    All of the adapters are plugged into a single USB hub. Since I can't distinguish between the adapters themselves, is there any way I can write a udev rule that fixes the name of each adapter based on which physical port on the hub the adapter is plugged into?

  • benathon
    benathon almost 10 years
    Sadly, most of the cheapo serial adapters out there don't have unique serials :(
  • Lucas
    Lucas over 9 years
    Thanks for citing those sources. The Linux USB Frequently Asked Questions was exactly what I needed.
  • Pithikos
    Pithikos about 9 years
    <VENDOR><delimeter><MODEL><delimeter><SERIAL>
  • JoGusto
    JoGusto over 3 years
    I wonder if @Chris made a typo in his rules. I'm running U18.04LTS and on my system, the USB names have a colon, like "1-8:1.5" instead of "1-8.1.5" as Chris wrote. I have used a form of his rule, with this modification, to name my Ethernet USB dongles with predictable, physical-position-based names: KERNEL=="eth*", KERNELS=="1-8:1.5", NAME=="ethu8p1d5" etc...
  • Michael Mrozek
    Michael Mrozek almost 3 years
    Very useful info. Your SiLabs links are broken now, so I'm not sure if this is the same tool, but it provides the same functionality and worked for me: silabs.com/content/usergenerated/asi/cloud/attachments/…