You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Non-Linux systems are relatively easy to determine what are and are not serial ports because they follow some semblance of rules for consistent device naming around serial and other devices. Linux doesn't.
And for all the crazy things Linux does with udev and sysfs and procfs to tell you all about the system, there's no simple way to say, "hi, what serial ports do you have?" Fortunately, on any Linux system modern enough for us to care about for this purpose, we do have some help. First, all serial devices are listed in /sys/class/tty. Here's mine on a Debian nettop system:
That's more like it! The indicated entries in /sys/class/tty are real serial ports that exist on my system at the moment. There's also a little more information in /proc:
You can determine how useful any of that is. I find it somewhat annoying to access this information using bash because I just don't have access to the tools to make use of it. I can use the sysfs interface though just fine from bash. I'd want to use python for sure if I were trying to use the older /proc interface.
That leads to the next problem: USB serial devices are wont to move around across reboots if you plug one into a USB port that is enumerated sooner on the next reboot. /dev/serial/by-id and /dev/serial/by-path contain entries that don't change upon reboots, supposedly. We'll use by-id since it's human-readable after a fashion. We'll implement something that basically works(ish) using bash:
#! /bin/bash# vim: ts=4 sw=4 tw=78 ft=shfordevicein /dev/tty*;do# See if it's a real device
devname=$(basename $device)if [ -d /sys/class/tty/$devname/device/driver ];then# According to sysfs, it is# Now see if there's a /dev/serial entry for itfordevserialin /dev/serial/by-id/*;doif [ "$(readlink -f $devserial)"="$device" ];then# Yes, use that instead
device="$devserial"fidone
devices="${devices:+$devices }$device"fidone# Show what we foundfordevin$devices;doecho"$dev"done
Doing the same in python looks like this and is a little more robust:
#! /usr/bin/env python3# vim: ts=4 sw=4 tw=78 ft=pythonimportosimportglobfrompprintimportpprint# The by-id paths are supposed to be stable across rebootsdeviceIdx= {}
forsymlinkinglob.glob('/dev/serial/by-id/*'):
deviceIdx[os.path.realpath(symlink)] =symlinkdevices= []
ttySysDir='/sys/class/tty'forttyinos.listdir(ttySysDir):
# VCs, ptys, etc don't have /sys/class/tty/<tty>/device/driver'sifos.path.exists(os.path.join(ttySysDir, tty, 'device/driver')):
# Appears to be a real serial devicedevice=os.path.join('/dev', tty)
ifdeviceindeviceIdx:
# use the stable namedevices.append(deviceIdx[device])
elifos.path.exists(device):
devices.append(device)
# Show what we foundpprint(devices)
Of course, you can see there that my FTDI adapters have serial numbers. The Keyspan doesn't, so if I plugged another one in, we'd be back to the same enumeration problem again—cheap crap usually doesn't have unique serial numbers. Which is funny because the Keyspan adapters were anything but cheap at the time they were sold!
Anyway, here's what by-path entries look like:
tjcarter@shiro:~$ ls /dev/serial/by-path/
pci-0000:00:1d.7-usb-0:6.1:1.0-port0@ pci-0000:00:1d.7-usb-0:6.3:1.0-port0@
pci-0000:00:1d.7-usb-0:6.2:1.0-port0@ pci-0000:00:1d.7-usb-0:6.3:1.0-port1@
Kind of misleading because at first glance it looks like those are PCI devices. You have to read backwards: Ports in a tree of USB devices in a tree of PCI devices. That's gonna make users' heads hurt. It makes MY head hurt, and I know what I'm looking at!
I don't mind presenting the user with the by-path device node when we have one, provided that we give them some human-readable output. The problem is that we don't have human-readable output for the UART drivers, other than that they're UARTs. It'd be real easy to figure out on my little nettop that the four UART ports don't physically exist if we translate that for the user so they know to look for physical serial ports on the computer. There aren't any. (I really ought to see if I can disable those in the BIOS, but left them on while I was working here.)
Anyway, this sufficiently defines the problem I think. The solution is there, but it isn't ever going to be pretty due to crappy hardware and software design. The best we can do is try to tell the user what we find in as much detail as we can to help them identify which ports are which, and make sure it works before we make any assumptions that it will.
The text was updated successfully, but these errors were encountered:
Non-Linux systems are relatively easy to determine what are and are not serial ports because they follow some semblance of rules for consistent device naming around serial and other devices. Linux doesn't.
And for all the crazy things Linux does with udev and sysfs and procfs to tell you all about the system, there's no simple way to say, "hi, what serial ports do you have?" Fortunately, on any Linux system modern enough for us to care about for this purpose, we do have some help. First, all serial devices are listed in
/sys/class/tty
. Here's mine on a Debian nettop system:Obviously not all of those are tty entries. But if you happen to limit the list a little, you'll see the ones that are:
That's more like it! The indicated entries in
/sys/class/tty
are real serial ports that exist on my system at the moment. There's also a little more information in/proc
:You can determine how useful any of that is. I find it somewhat annoying to access this information using bash because I just don't have access to the tools to make use of it. I can use the sysfs interface though just fine from bash. I'd want to use python for sure if I were trying to use the older /proc interface.
That leads to the next problem: USB serial devices are wont to move around across reboots if you plug one into a USB port that is enumerated sooner on the next reboot.
/dev/serial/by-id
and/dev/serial/by-path
contain entries that don't change upon reboots, supposedly. We'll useby-id
since it's human-readable after a fashion. We'll implement something that basically works(ish) using bash:Doing the same in python looks like this and is a little more robust:
The output of these commands:
Of course, you can see there that my FTDI adapters have serial numbers. The Keyspan doesn't, so if I plugged another one in, we'd be back to the same enumeration problem again—cheap crap usually doesn't have unique serial numbers. Which is funny because the Keyspan adapters were anything but cheap at the time they were sold!
Anyway, here's what
by-path
entries look like:Kind of misleading because at first glance it looks like those are PCI devices. You have to read backwards: Ports in a tree of USB devices in a tree of PCI devices. That's gonna make users' heads hurt. It makes MY head hurt, and I know what I'm looking at!
I don't mind presenting the user with the by-path device node when we have one, provided that we give them some human-readable output. The problem is that we don't have human-readable output for the UART drivers, other than that they're UARTs. It'd be real easy to figure out on my little nettop that the four UART ports don't physically exist if we translate that for the user so they know to look for physical serial ports on the computer. There aren't any. (I really ought to see if I can disable those in the BIOS, but left them on while I was working here.)
Anyway, this sufficiently defines the problem I think. The solution is there, but it isn't ever going to be pretty due to crappy hardware and software design. The best we can do is try to tell the user what we find in as much detail as we can to help them identify which ports are which, and make sure it works before we make any assumptions that it will.
The text was updated successfully, but these errors were encountered: