How to find which serial port is in use?
The Question: I plugged in a device (i.e. GSM modem) through a serial port (a.k.a. RS-232), and I need to see with which file in /dev/ filesystem this device was tied up, to be able to communicate with it. Unfortunately there is no newly created file in /dev/ nor can be seen anything in dmesg output. So this seems to be a hard question. Background: I had never worked with a serial device, so yesterday, when there appeared a need, I tried to Google it but couldn’t find anything helpful. I spent a few hours in seek, and I want to share a found answer as it could be helpful for someone.
I like using those bits to detect a device — I’ll probably use that, although I’ve many devices that only use the Tx/Rx pins, I could probably hack them to hold the CTS line high for that.
2 Answers 2
Unfortunately serial ports are non-PlugNPlay, so kernel doesn’t know which device was plugged in. After reading a HowTo tutorial I’ve got the working idea.
The /dev/ directory of unix like OSes contains files named as ttySn (with n being a number). Most of them doesn’t correspond to existing devices. To find which ones do, issue a command:
$ dmesg | grep ttyS [ 0.872181] 00:06: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A [ 0.892626] 00:07: ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A [ 0.915797] 0000:01:01.0: ttyS4 at I/O 0x9800 (irq = 19) is a ST16650V2 [ 0.936942] 0000:01:01.1: ttyS5 at I/O 0x9c00 (irq = 18) is a ST16650V2
Above is an example output of my PC. You can see the initialization of a few serial ports:
ttyS0 , ttyS1 , ttyS4 , ttyS5 .
One of them is going to have a positive voltage upon a device plugged in. So by comparing the content of the file /proc/tty/driver/serial with and without the device plugged in we can easily find the ttyS related to our device. So, now do:
$ sudo cat /proc/tty/driver/serial> /tmp/1
$ sudo cat /proc/tty/driver/serial> /tmp/2
Next check the difference between the two files. Below is an output of my PC:
$ diff /tmp/1 /tmp/2 2c2 < 0: uart:16550A port:000003F8 irq:4 tx:6 rx:0 --- >0: uart:16550A port:000003F8 irq:4 tx:6 rx:0 CTS|DSR
By comparing the three numbers with the dmesg output we can determine which one is the port:
[ 0.872181] 00:06: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
Hence, our device is /dev/ttyS0 , mission accomplished!
@user3019105 sorry, I’ve never faced such issue. From what I understand, dmesg should have a message about assigning irq to a serial device. And if it didn’t, I’d assume some hardware problem. That’s just an assumption, and might be wrong, but if I’d debug such a problem, I’d start research with the idea in mind that serial port should’ve been initialized.
Since I move from machine to machine I have defined an alias to show me the serial ports on this machine. It works on Ubuntu family systems with systemd. alias ports=’udevadm info —export-db |grep -i «^n: ttyu»‘
You can also use setserial to get and set serial port information, in addition to dmesg .
setserial
# install it sudo apt update sudo apt install setserial # Display (and sort) serial information for all /dev/ttyS* devices setserial -g /dev/ttyS* | sort -V # Display (and sort) serial information for all /dev/ttyUSB* devices setserial -g /dev/ttyUSB* | sort -V # To see extra info. such as the baud rate too, add `-G`: setserial -g -G /dev/ttyS* | sort -V setserial -g -G /dev/ttyUSB* | sort -V
Sample run and output. Notice that /dev/ttyS4 is the only port that appears to be alive and not «unknown» (whatever that means). (This /dev/ttyS4 device also shows up in the Arduino IDE even though it’s not an Arduino and I don’t know what it is.):
$ setserial -g -G /dev/ttyS* | sort -V /dev/ttyS0 uart unknown port 0x03f8 irq 4 baud_base 115200 spd_normal skip_test /dev/ttyS1 uart unknown port 0x02f8 irq 3 baud_base 115200 spd_normal skip_test /dev/ttyS2 uart unknown port 0x03e8 irq 4 baud_base 115200 spd_normal skip_test /dev/ttyS3 uart unknown port 0x02e8 irq 3 baud_base 115200 spd_normal /dev/ttyS4 uart 16550A port 0x3060 irq 19 baud_base 115200 spd_normal skip_test /dev/ttyS5 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS6 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS7 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS8 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS9 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS10 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS11 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS12 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS13 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS14 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS15 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS16 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS17 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS18 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS19 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS20 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS21 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS22 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS23 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS24 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS25 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS26 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS27 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS28 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS29 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS30 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal /dev/ttyS31 uart unknown port 0x0000 irq 0 baud_base 0 spd_normal
dmesg
Use dmesg to also see some of this information:
# Show UART information, incl. baudrate, for /dev/ttyS* devices. dmesg | grep ttyS # Show UART information, incl. baudrate, for /dev/ttyUSB* devices. dmesg | grep ttyUSB
$ dmesg | grep ttyS [ 1.624447] 0000:00:16.3: ttyS4 at I/O 0x3060 (irq = 19, base_baud = 115200) is a 16550A
Even better, watch the dmesg kernel ring buffer output live with the -w option:
Now, unplug your USB device you’re trying to identify and plug it back in to see which device it is, based on the messages that show up in the dmesg output, live.
How to find all serial devices (ttyS, ttyUSB, ..) on Linux without opening them?
What is the proper way to get a list of all available serial ports/devices on a Linux system? In other words, when I iterate over all devices in /dev/ , how do I tell which ones are serial ports in the classic way, that is, those usually supporting baud rates and RTS/CTS flow control? The solution would be coded in C. I ask because I am using a third-party library that does this clearly wrong: It appears to only iterate over /dev/ttyS* . The problem is that there are, for instance, serial ports over USB (provided by USB-RS232 adapters), and those are listed under /dev/ttyUSB*. And reading the Serial-HOWTO at Linux.org, I get the idea that there’ll be other name spaces as well, as time comes. So I need to find the official way to detect serial devices. The problem is that none appears to be documented, or I can’t find it. I imagine one way would be to open all files from /dev/tty* and call a specific ioctl() on them that is only available on serial devices. Would that be a good solution, though?
Update
hrickards suggested to look at the source for «setserial». Its code does exactly what I had in mind: First, it opens a device with:
fd = open (path, O_RDWR | O_NONBLOCK)
ioctl (fd, TIOCGSERIAL, &serinfo)
If that call returns no error, then it’s a serial device, apparently. I found similar code in Serial Programming/termios, which suggested to also add the O_NOCTTY option. There is one problem with this approach, though: When I tested this code on BSD Unix (that is, Mac OS X), it worked as well. However, serial devices that are provided through Bluetooth cause the system (driver) to try to connect to the Bluetooth device, which takes a while before it’ll return with a timeout error. This is caused by just opening the device. And I can imagine that similar things can happen on Linux as well — ideally, I should not need to open the device to figure out its type. I wonder if there’s also a way to invoke ioctl functions without an open, or open a device in a way that it does not cause connections to be made? What should I do?