Scenario:
- multiple USB devices plugged via hub to a host (Linux OS based),
- multiple services/programs interacting with TTY running on top (e.g. GPSd)
Problem:
At boot TTY are randomly assigned to devices causing depending services/programs instabilities. They could indeed fail to start because of different TTY configurations.
Solution:
- Assign un-mutable TTY names to USB devices by creating symbolic links of physical devices
- Configure then services/programs to point to these symbolic TTYs
Therefore, the short answer is: customize udev
rules.
udev
allows a Linux system to use consistent names for devices such as removable drives and printers,
which in turn allows users to experience predictable behavior when devices are added or removed from the system.
In synthesis, it represents Linux dynamic device manager.
udev
consists of:
- a configuration file
/etc/udev/udev.conf
, - permission files, and
- rules files
Rules files are used to determine the TTY used for removable drives currently available in the system. Every line within rule files defines how a specific device attribute is mapped to a dedicated device file.
The default udev
rules file is /etc/udev/rules.d/50-udev.rules
and should not be modified by a user.
To create new rules, add a new file in the same directory (/etc/udev/rules.d/
) keeping in mind the following conventions:
- All rules files must have a filename that ends with the
.rules
extension - Files are read in ascending order
Therefore, to create a customized file read before the default one, just type for example /etc/udev/rules.d/49-my.rules
.
Execute lsusb
to see all the currently detected USB devices printed out with an essential amount of info.
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 002: ID 2109:2812
Bus 002 Device 003: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 003 Device 002: ID 2109:0812
Bus 002 Device 004: ID 1546:01a8 U-Blox AG
Bus 002 Device 005: ID 2341:8036 Arduino SA Leonardo (CDC ACM, HID)
Bus 002 Device 006: ID 12d1:14db Huawei Technologies Co., Ltd. E353/E3131
Bus 002 Device 007: ID 0529:0001 Aladdin Knowledge Systems HASP v0.06
In this case, two USB devices are connected via hub to the host:
- a USB GPS Receiver U-Blox AG, and
- an Arduino platform
This means, they could be connected to either the device /dev/ttyACM0
or /dev/ttyACM1
(randomly selected).
Besides, their services require a static configuration of the expected TTY.
Therefore, a solution here is creating a customized udev
rule assigning the U-Blox device to a symbolic /dev/ttyGPS
.
Execute udevadm info -a -p $(udevadm info -q path -n /dev/ttyACM0)
to increase the verbosity of the USB details.
The output will look like the one reported below:
looking at device '/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3.2/2-3.2:1.0/tty/ttyACM0':
KERNEL=="ttyACM0"
SUBSYSTEM=="tty"
DRIVER==""
[...]
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3.2':
KERNELS=="2-3.2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 2"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="c0"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="40406"
ATTRS{idVendor}=="1546"
ATTRS{idProduct}=="01a8"
ATTRS{bcdDevice}=="0201"
ATTRS{bDeviceClass}=="02"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="12"
ATTRS{busnum}=="2"
ATTRS{devnum}=="4"
ATTRS{version}==" 1.10"
ATTRS{maxchild}=="0"
ATTRS{quirks}=="0x0"
ATTRS{authorized}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="u-blox AG - www.u-blox.com"
ATTRS{product}=="u-blox GNSS receiver"
[...]
Assuming I need a rule to be read before the default ones, open the file /etc/udev/rules.d/49-custom.rules
and write
down the deducted information as for the GPS case below:
# U-Blox symbolic link of the driver to a customized one
KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a8", SYMLINK="ttyGPS"
Hence, services/programs configurations must be setup accordingly.
Considering the GPS case, make sure the configuration file (/etc/sysconfig/gpsd
or /etc/default/gpsd
) contains the following line: DEVICE="/dev/ttyGPS"
For the Arduino case, modify the configuration file opportunely.
Restart the machine to apply the changes.
- https://www.redhat.com/magazine/002dec04/features/udev/
- http://weininger.net/how-to-write-udev-rules-for-usb-devices.html
- http://stackoverflow.com/questions/7986034/linux-how-to-force-a-usb-device-to-use-the-same-ttyusb-number
- http://noctis.de/ramblings/linux/49-howto-fixed-name-for-a-udev-device.html
- https://forums.anandtech.com/threads/is-there-a-way-to-force-a-specific-serial-usb-device-to-a-specifc-tty.2253683/
What is the solution if multiple devices belong to same vendor id and product id ?