26 Jun 2023 - tsp
Last update 26 Jun 2023
5 mins
So what’s the problem that is getting solved? Usually when one attaches an
USB to serial (or RS485) adapter to ones computer it
gets assigned a teletype interface (tty).
On FreeBSD those are usually called /dev/cuaU
for traditional callout ports, /dev/ttyU
for callin ports. When accessing serial ports it does not matter which of those interfaces
are used, the distinction is required for modem style (teletype) devices. The assignment of
the device numbers usually occurs in sequence. Whenever one changes the order of USB devices
on an hub or host controller or they are attached or detached with different timing the device
numbering changes. This is not a huge problem when trying to use those devices on demand
manually since one can see which device got attached or not. This is a problem when accessing
devices programmatically even over system reboots - or when accessing them even over system
changes. When using teletype devices also for communication
(for example for UMTS/LTE interfaces) one can even
force a system offline when devices get renumbered and loose all connectivity to remote devices.
The solution is simple - one wants to have the same device filename for the same device every
time. Unfortunately there is no way to access the devices by their physical port or assign a fixed
name in the form of /dev/ttyU
or /dev/cuaU
to devices. Those will change over time
and hardware configuration changes. On the other hand devices can usually be distinguished by
their serial number. Each USB device should have a unique serial number (note that some devices
like some ZTE USB modems as well as CH340 USB to serial converters do not have a unique serial
number so this is still not possible as described here). The idea is to create a symbolic link
to the cuaU
device based on the serial number of the device using a simple devd
configuration.
A very simple method for CP2102N based devices looks like the following in /etc/devd.conf
attach 1000 {
match "vendor" "0x10c4";
match "product" "0xea60";
action "ln -sf /dev/cua$ttyname /dev/cp2102n_$sernum && ln -sf /dev/cua$ttyname.lock /dev/cp2102n_$sernum.lock && ln -sf /dev/cua$ttyname.init /dev/cp2102n_$sernum.init";
};
notify 1000 {
match "vendor" "0x10c4";
match "product" "0xea60";
match "type" "DETACH";
match "subsystem" "DEVICE";
action "rm /dev/cp2102n_$sernum && rm /dev/cp2102n_$sernum.init && rm /dev/cp2102n_$sernum.lock";
};
The idea of those scripts is very simple. Whenever a device with vendor ID 0x10c4
and product 0xea60
-
which is the default for CP2102N devices - is attached to the system - after most other rules have been
executed (this is realized by the large rule number) the attach
rule simply generates symbolic links to
the cuaU
, the lock
and init
device. Those symbolic links simply include the serial number
of the device. It’s assumed that all devices have been configured either with Simplcity Studio to a custom
but unique (at least for the setup) serial or have been configured to use the internal unique serial for CP2102N
devices. Note that it’s not possible to create directories using devd
.
The notify
rule is used to delete the symbolic links whenever a device of the same family
is detached again.
This approach works also for other vendor and device IDs of course. As a result the script above generates links to device files like the following:
/dev/cp2102n_1aa2d3624a83ed118437ae5f9d1cc348
/dev/cp2102n_1aa2d3624a83ed118437ae5f9d1cc348.lock
/dev/cp2102n_1aa2d3624a83ed118437ae5f9d1cc348.init
I tried to use the same approach with ZTE modems. The main problem here was that they exposed multiple
functions as tty
devices - each of them having the same $ttyname
. Unfortunately I was not
able to identify an environment variable for each of the functions to be used in the attach
rule.
Since I had not much time to do further investigation so I decided to solve the problem with a dirty hack.
This works since I knew that the ZTE USB modem I used on my embedded systems expose three interfaces - and
I’ve attached only one device with the same vendor and device ID per system.
The quickest hacked approach that came to my mind was to write two shell scripts that create symbolic links
for all four devices exposed under a given $ttyname
base name. This script is called makemodemlinks.sh
#!/bin/sh
CANDIDATES=`ls /dev/cua${1}*`
for cand in ${CANDIDATES}; do
ismodem=0
case ${cand} in
"/dev/cuaU"?"."?"."*) ismodem=1;;
"/dev/cuaU"?"."?) ismodem=2;;
*) ismodem=0;;
esac
if [ ${ismodem} -eq 1 ]; then
idpart=`echo ${cand} | tail -c 8`
aliaspath="/dev/usbModem${idpart}"
if [ ! -e ${aliaspath} ]; then
ln -sf ${cand} ${aliaspath}
chgrp dialer ${aliaspath}
chgrp -h dialer ${aliaspath}
fi
fi
if [ ${ismodem} -eq 2 ]; then
idpart=`echo ${cand} | tail -c 3`
aliaspath="/dev/usbModem${idpart}"
if [ ! -e ${aliaspath} ]; then
ln -sf ${cand} ${aliaspath}
chgrp dialer ${aliaspath}
chgrp -h dialer ${aliaspath}
fi
fi
done
The $ttyname
will be passed as first argument. The script then locates all device files
with the pattern /dev/cuaUX.Y
as well as /dev/cuaUX.Y.lock
and /dev/cuaUX.Y.init
.
All files are then linked under the basename /dev/usbModem
. The major number X
is stripped
so only one device can be attached using this script. In addition a deletion script rmmodemlinks.sh
is used:
#!/bin/sh
CANDIDATES=`ls /dev/usbModem.*`
for cand in ${CANDIDATES}; do
rm ${cand}
done
I simply call those scripts from devd.conf
whenever the ZTE modem is attached or detached:
attach 1000 {
match "vendor" "0x19d2";
match "product" "0x0031";
action "/root/makemodemlinks.sh $ttyname";
};
notify 1000 {
match "vendor" "0x19d2";
match "product" "0x0031";
match "type" "DETACH";
match "subsystem" "DEVICE";
action "/root/rmmodemlinks.sh $ttyname";
};
There is for sure a way better way to handle USB modems in this case - the major problem having been the ZTE modems not supplying a unique serial number. But for the acute problem the scripts had been sufficient.
This article is tagged:
Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)
This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/